blob: e1fa366fa7cd43ce2999c0f2a6f896a79d75e302 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkDraw.h"
11#include "SkBlitter.h"
12#include "SkBounder.h"
13#include "SkCanvas.h"
14#include "SkColorPriv.h"
15#include "SkDevice.h"
bungeman@google.com2211b622012-01-13 15:02:58 +000016#include "SkFixed.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkMaskFilter.h"
18#include "SkPaint.h"
19#include "SkPathEffect.h"
reed@google.com045e62d2011-10-24 12:19:46 +000020#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkRasterizer.h"
22#include "SkScan.h"
23#include "SkShader.h"
24#include "SkStroke.h"
25#include "SkTemplatesPriv.h"
reed@google.com32e5d972011-07-27 18:25:57 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
28
29#include "SkAutoKern.h"
30#include "SkBitmapProcShader.h"
31#include "SkDrawProcs.h"
32
33//#define TRACE_BITMAP_DRAWS
34
reed@android.com8a1c16f2008-12-17 15:59:43 +000035#define kBlitterStorageLongCount (sizeof(SkBitmapProcShader) >> 2)
36
reed@google.comfd4236e2011-07-25 21:16:22 +000037/** Helper for allocating small blitters on the stack.
38 */
reed@google.com40c2ba22011-07-25 19:41:22 +000039class SkAutoBlitterChoose : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040public:
reed@google.com1d6ee0b2011-07-05 17:44:56 +000041 SkAutoBlitterChoose() {
42 fBlitter = NULL;
43 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix,
45 const SkPaint& paint) {
46 fBlitter = SkBlitter::Choose(device, matrix, paint,
47 fStorage, sizeof(fStorage));
48 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000049
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 ~SkAutoBlitterChoose();
51
52 SkBlitter* operator->() { return fBlitter; }
53 SkBlitter* get() const { return fBlitter; }
54
reed@google.com1d6ee0b2011-07-05 17:44:56 +000055 void choose(const SkBitmap& device, const SkMatrix& matrix,
56 const SkPaint& paint) {
57 SkASSERT(!fBlitter);
58 fBlitter = SkBlitter::Choose(device, matrix, paint,
59 fStorage, sizeof(fStorage));
60 }
61
reed@android.com8a1c16f2008-12-17 15:59:43 +000062private:
63 SkBlitter* fBlitter;
64 uint32_t fStorage[kBlitterStorageLongCount];
65};
66
67SkAutoBlitterChoose::~SkAutoBlitterChoose() {
68 if ((void*)fBlitter == (void*)fStorage) {
69 fBlitter->~SkBlitter();
70 } else {
71 SkDELETE(fBlitter);
72 }
73}
74
reed@google.com40c2ba22011-07-25 19:41:22 +000075/**
76 * Since we are providing the storage for the shader (to avoid the perf cost
77 * of calling new) we insist that in our destructor we can account for all
78 * owners of the shader.
79 */
80class SkAutoBitmapShaderInstall : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081public:
reed@google.com40c2ba22011-07-25 19:41:22 +000082 SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint)
83 : fPaint(paint) /* makes a copy of the paint */ {
84 fPaint.setShader(SkShader::CreateBitmapShader(src,
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
86 fStorage, sizeof(fStorage)));
reed@google.com40c2ba22011-07-25 19:41:22 +000087 // we deliberately left the shader with an owner-count of 2
88 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 }
reed@google.com82065d62011-02-07 15:30:46 +000090
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 ~SkAutoBitmapShaderInstall() {
reed@google.com40c2ba22011-07-25 19:41:22 +000092 SkShader* shader = fPaint.getShader();
93 // since we manually destroy shader, we insist that owners == 2
94 SkASSERT(2 == shader->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000095
reed@google.com40c2ba22011-07-25 19:41:22 +000096 fPaint.setShader(NULL); // unref the shader by 1
reed@android.com8a1c16f2008-12-17 15:59:43 +000097
reed@google.com40c2ba22011-07-25 19:41:22 +000098 // now destroy to take care of the 2nd owner-count
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 if ((void*)shader == (void*)fStorage) {
100 shader->~SkShader();
101 } else {
102 SkDELETE(shader);
103 }
104 }
reed@google.com82065d62011-02-07 15:30:46 +0000105
reed@google.com40c2ba22011-07-25 19:41:22 +0000106 // return the new paint that has the shader applied
107 const SkPaint& paintWithShader() const { return fPaint; }
108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109private:
reed@google.com40c2ba22011-07-25 19:41:22 +0000110 SkPaint fPaint; // copy of caller's paint (which we then modify)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 uint32_t fStorage[kBlitterStorageLongCount];
112};
113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114///////////////////////////////////////////////////////////////////////////////
115
reed@android.comf2b98d62010-12-20 18:26:13 +0000116SkDraw::SkDraw() {
117 sk_bzero(this, sizeof(*this));
118}
119
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120SkDraw::SkDraw(const SkDraw& src) {
121 memcpy(this, &src, sizeof(*this));
122}
123
124///////////////////////////////////////////////////////////////////////////////
125
126typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
127
128static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +0000129 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130}
131
132static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
133
134static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
135 sk_memset32((uint32_t*)pixels, data, bytes >> 2);
136}
137
138static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
139 sk_memset16((uint16_t*)pixels, data, bytes >> 1);
140}
141
142static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
143 memset(pixels, data, bytes);
144}
145
146static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap,
147 const SkPaint& paint,
148 uint32_t* data) {
149 // todo: we can apply colorfilter up front if no shader, so we wouldn't
150 // need to abort this fastpath
151 if (paint.getShader() || paint.getColorFilter()) {
152 return NULL;
153 }
154
reed@android.com845fdac2009-06-23 03:01:32 +0000155 SkXfermode::Mode mode;
mike@reedtribe.orgbe2aa2a2011-11-17 02:32:04 +0000156 if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 return NULL;
158 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000161
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 // collaps modes based on color...
reed@android.com845fdac2009-06-23 03:01:32 +0000163 if (SkXfermode::kSrcOver_Mode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 unsigned alpha = SkColorGetA(color);
165 if (0 == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000166 mode = SkXfermode::kDst_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 } else if (0xFF == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000168 mode = SkXfermode::kSrc_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 }
170 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000171
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 switch (mode) {
reed@android.com845fdac2009-06-23 03:01:32 +0000173 case SkXfermode::kClear_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174// SkDebugf("--- D_Clear_BitmapXferProc\n");
175 return D_Clear_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000176 case SkXfermode::kDst_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177// SkDebugf("--- D_Dst_BitmapXferProc\n");
178 return D_Dst_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000179 case SkXfermode::kSrc_Mode: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000181 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 */
183 SkPMColor pmc = SkPreMultiplyColor(color);
184 switch (bitmap.config()) {
185 case SkBitmap::kARGB_8888_Config:
186 if (data) {
187 *data = pmc;
188 }
189// SkDebugf("--- D32_Src_BitmapXferProc\n");
190 return D32_Src_BitmapXferProc;
191 case SkBitmap::kARGB_4444_Config:
192 if (data) {
193 *data = SkPixel32ToPixel4444(pmc);
194 }
195// SkDebugf("--- D16_Src_BitmapXferProc\n");
196 return D16_Src_BitmapXferProc;
197 case SkBitmap::kRGB_565_Config:
198 if (data) {
199 *data = SkPixel32ToPixel16(pmc);
200 }
201// SkDebugf("--- D16_Src_BitmapXferProc\n");
202 return D16_Src_BitmapXferProc;
203 case SkBitmap::kA8_Config:
204 if (data) {
205 *data = SkGetPackedA32(pmc);
206 }
207// SkDebugf("--- DA8_Src_BitmapXferProc\n");
208 return DA8_Src_BitmapXferProc;
209 default:
210 break;
211 }
212 break;
213 }
214 default:
215 break;
216 }
217 return NULL;
218}
219
220static void CallBitmapXferProc(const SkBitmap& bitmap, const SkIRect& rect,
221 BitmapXferProc proc, uint32_t procData) {
222 int shiftPerPixel;
223 switch (bitmap.config()) {
224 case SkBitmap::kARGB_8888_Config:
225 shiftPerPixel = 2;
226 break;
227 case SkBitmap::kARGB_4444_Config:
228 case SkBitmap::kRGB_565_Config:
229 shiftPerPixel = 1;
230 break;
231 case SkBitmap::kA8_Config:
232 shiftPerPixel = 0;
233 break;
234 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000235 SkDEBUGFAIL("Can't use xferproc on this config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 return;
237 }
238
239 uint8_t* pixels = (uint8_t*)bitmap.getPixels();
240 SkASSERT(pixels);
241 const size_t rowBytes = bitmap.rowBytes();
242 const int widthBytes = rect.width() << shiftPerPixel;
243
244 // skip down to the first scanline and X position
245 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
246 for (int scans = rect.height() - 1; scans >= 0; --scans) {
247 proc(pixels, widthBytes, procData);
248 pixels += rowBytes;
249 }
250}
251
252void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000253 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254
reed@google.com045e62d2011-10-24 12:19:46 +0000255 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 return;
257 }
258
259 SkIRect devRect;
260 devRect.set(0, 0, fBitmap->width(), fBitmap->height());
261 if (fBounder && !fBounder->doIRect(devRect)) {
262 return;
263 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000264
reed@google.com045e62d2011-10-24 12:19:46 +0000265 if (fRC->isBW()) {
266 /* If we don't have a shader (i.e. we're just a solid color) we may
267 be faster to operate directly on the device bitmap, rather than invoking
268 a blitter. Esp. true for xfermodes, which require a colorshader to be
269 present, which is just redundant work. Since we're drawing everywhere
270 in the clip, we don't have to worry about antialiasing.
271 */
272 uint32_t procData = 0; // to avoid the warning
273 BitmapXferProc proc = ChooseBitmapXferProc(*fBitmap, paint, &procData);
274 if (proc) {
275 if (D_Dst_BitmapXferProc == proc) { // nothing to do
276 return;
277 }
278
279 SkRegion::Iterator iter(fRC->bwRgn());
280 while (!iter.done()) {
281 CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData);
282 iter.next();
283 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 return;
285 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 }
reed@google.com045e62d2011-10-24 12:19:46 +0000287
288 // normal case: use a blitter
289 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
290 SkScan::FillIRect(devRect, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291}
292
293///////////////////////////////////////////////////////////////////////////////
294
295struct PtProcRec {
296 SkCanvas::PointMode fMode;
297 const SkPaint* fPaint;
298 const SkRegion* fClip;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 const SkRasterClip* fRC;
reed@google.coma76de3d2011-01-13 18:30:42 +0000300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 // computed values
302 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
305 SkBlitter*);
306
307 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000308 const SkRasterClip*);
309 Proc chooseProc(SkBlitter** blitter);
310
311private:
312 SkAAClipBlitterWrapper fWrapper;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313};
314
315static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
316 int count, SkBlitter* blitter) {
317 SkASSERT(rec.fClip->isRect());
318 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000321 int x = SkScalarFloorToInt(devPts[i].fX);
322 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 if (r.contains(x, y)) {
324 blitter->blitH(x, y, 1);
325 }
326 }
327}
328
329static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
330 const SkPoint devPts[], int count,
331 SkBlitter* blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +0000332 SkASSERT(rec.fRC->isRect());
333 const SkIRect& r = rec.fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 uint32_t value;
335 const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
336 SkASSERT(bitmap);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000337
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 uint16_t* addr = bitmap->getAddr16(0, 0);
339 int rb = bitmap->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000342 int x = SkScalarFloorToInt(devPts[i].fX);
343 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 if (r.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
346 }
347 }
348}
349
reed@google.com2d47a212012-11-29 21:01:00 +0000350static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
351 const SkPoint devPts[], int count,
352 SkBlitter* blitter) {
353 SkASSERT(rec.fRC->isRect());
354 const SkIRect& r = rec.fRC->getBounds();
355 uint32_t value;
356 const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
357 SkASSERT(bitmap);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000358
reed@google.com2d47a212012-11-29 21:01:00 +0000359 SkPMColor* addr = bitmap->getAddr32(0, 0);
360 int rb = bitmap->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000361
reed@google.com2d47a212012-11-29 21:01:00 +0000362 for (int i = 0; i < count; i++) {
363 int x = SkScalarFloorToInt(devPts[i].fX);
364 int y = SkScalarFloorToInt(devPts[i].fY);
365 if (r.contains(x, y)) {
366 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
367 }
368 }
369}
370
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
372 int count, SkBlitter* blitter) {
373 for (int i = 0; i < count; i++) {
374 int x = SkScalarFloor(devPts[i].fX);
375 int y = SkScalarFloor(devPts[i].fY);
376 if (rec.fClip->contains(x, y)) {
377 blitter->blitH(x, y, 1);
378 }
379 }
380}
381
382static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
383 int count, SkBlitter* blitter) {
384 for (int i = 0; i < count; i += 2) {
reed@google.com045e62d2011-10-24 12:19:46 +0000385 SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386 }
387}
388
389static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
390 int count, SkBlitter* blitter) {
391 for (int i = 0; i < count - 1; i++) {
reed@google.com045e62d2011-10-24 12:19:46 +0000392 SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 }
394}
395
396// aa versions
397
398static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
399 int count, SkBlitter* blitter) {
400 for (int i = 0; i < count; i += 2) {
reed@google.com045e62d2011-10-24 12:19:46 +0000401 SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 }
403}
404
405static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
406 int count, SkBlitter* blitter) {
407 for (int i = 0; i < count - 1; i++) {
reed@google.com045e62d2011-10-24 12:19:46 +0000408 SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 }
410}
411
412// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
413
414static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
415 int count, SkBlitter* blitter) {
416 const SkFixed radius = rec.fRadius;
417 for (int i = 0; i < count; i++) {
418 SkFixed x = SkScalarToFixed(devPts[i].fX);
419 SkFixed y = SkScalarToFixed(devPts[i].fY);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000420
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 SkXRect r;
422 r.fLeft = x - radius;
423 r.fTop = y - radius;
424 r.fRight = x + radius;
425 r.fBottom = y + radius;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000426
reed@google.com045e62d2011-10-24 12:19:46 +0000427 SkScan::FillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 }
429}
430
431static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
432 int count, SkBlitter* blitter) {
433 const SkFixed radius = rec.fRadius;
434 for (int i = 0; i < count; i++) {
435 SkFixed x = SkScalarToFixed(devPts[i].fX);
436 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 SkXRect r;
439 r.fLeft = x - radius;
440 r.fTop = y - radius;
441 r.fRight = x + radius;
442 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000443
reed@google.com045e62d2011-10-24 12:19:46 +0000444 SkScan::AntiFillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 }
446}
447
reed@android.comb4f404a2009-07-10 17:02:17 +0000448// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
reed@google.com045e62d2011-10-24 12:19:46 +0000450 const SkMatrix* matrix, const SkRasterClip* rc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 if (paint.getPathEffect()) {
452 return false;
453 }
454 SkScalar width = paint.getStrokeWidth();
455 if (0 == width) {
456 fMode = mode;
457 fPaint = &paint;
reed@google.com045e62d2011-10-24 12:19:46 +0000458 fClip = NULL;
459 fRC = rc;
reed@google.com2d47a212012-11-29 21:01:00 +0000460 fRadius = SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 return true;
462 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000463 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
464 matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
466 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
467 if (SkScalarNearlyZero(sx - sy)) {
468 if (sx < 0) {
469 sx = -sx;
470 }
471
472 fMode = mode;
473 fPaint = &paint;
reed@google.com045e62d2011-10-24 12:19:46 +0000474 fClip = NULL;
475 fRC = rc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
477 return true;
478 }
479 }
480 return false;
481}
482
reed@google.com045e62d2011-10-24 12:19:46 +0000483PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
reed@android.comb4f404a2009-07-10 17:02:17 +0000484 Proc proc = NULL;
reed@google.coma76de3d2011-01-13 18:30:42 +0000485
reed@google.com045e62d2011-10-24 12:19:46 +0000486 SkBlitter* blitter = *blitterPtr;
487 if (fRC->isBW()) {
488 fClip = &fRC->bwRgn();
489 } else {
490 fWrapper.init(*fRC, blitter);
491 fClip = &fWrapper.getRgn();
492 blitter = fWrapper.getBlitter();
493 *blitterPtr = blitter;
494 }
495
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 // for our arrays
497 SkASSERT(0 == SkCanvas::kPoints_PointMode);
498 SkASSERT(1 == SkCanvas::kLines_PointMode);
499 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
500 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
501
reed@google.com2d47a212012-11-29 21:01:00 +0000502 if (fPaint->isAntiAlias()) {
503 if (0 == fPaint->getStrokeWidth()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 static const Proc gAAProcs[] = {
505 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
506 };
507 proc = gAAProcs[fMode];
reed@google.com2d47a212012-11-29 21:01:00 +0000508 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
509 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
510 proc = aa_square_proc;
511 }
512 } else { // BW
513 if (fRadius <= SK_FixedHalf) { // small radii and hairline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
515 uint32_t value;
516 const SkBitmap* bm = blitter->justAnOpaqueColor(&value);
reed@google.com2d47a212012-11-29 21:01:00 +0000517 if (bm && SkBitmap::kRGB_565_Config == bm->config()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 proc = bw_pt_rect_16_hair_proc;
reed@google.com2d47a212012-11-29 21:01:00 +0000519 } else if (bm && SkBitmap::kARGB_8888_Config == bm->config()) {
520 proc = bw_pt_rect_32_hair_proc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 } else {
522 proc = bw_pt_rect_hair_proc;
523 }
524 } else {
525 static Proc gBWProcs[] = {
526 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
527 };
528 proc = gBWProcs[fMode];
529 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 } else {
531 proc = bw_square_proc;
532 }
533 }
534 return proc;
535}
536
537static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode,
538 size_t count, const SkPoint pts[],
539 const SkPaint& paint, const SkMatrix& matrix) {
540 SkIRect ibounds;
541 SkRect bounds;
542 SkScalar inset = paint.getStrokeWidth();
543
544 bounds.set(pts, count);
545 bounds.inset(-inset, -inset);
546 matrix.mapRect(&bounds);
547
548 bounds.roundOut(&ibounds);
549 return bounder->doIRect(ibounds);
550}
551
552// each of these costs 8-bytes of stack space, so don't make it too large
553// must be even for lines/polygon to work
554#define MAX_DEV_PTS 32
555
556void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000557 const SkPoint pts[], const SkPaint& paint,
558 bool forceUseDevice) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 // if we're in lines mode, force count to be even
560 if (SkCanvas::kLines_PointMode == mode) {
561 count &= ~(size_t)1;
562 }
563
564 if ((long)count <= 0) {
565 return;
566 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000567
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 SkASSERT(pts != NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000569 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000570
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000572 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 return;
574 }
575
reed@google.comfd4236e2011-07-25 21:16:22 +0000576 if (fBounder) {
577 if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
578 return;
579 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000580
reed@google.comfd4236e2011-07-25 21:16:22 +0000581 // clear the bounder and call this again, so we don't invoke the bounder
582 // later if we happen to call ourselves for drawRect, drawPath, etc.
583 SkDraw noBounder(*this);
584 noBounder.fBounder = NULL;
585 noBounder.drawPoints(mode, count, pts, paint, forceUseDevice);
586 return;
587 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000588
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 PtProcRec rec;
reed@google.com045e62d2011-10-24 12:19:46 +0000590 if (!forceUseDevice && rec.init(mode, paint, fMatrix, fRC)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
592
593 SkPoint devPts[MAX_DEV_PTS];
594 const SkMatrix* matrix = fMatrix;
595 SkBlitter* bltr = blitter.get();
reed@google.com045e62d2011-10-24 12:19:46 +0000596 PtProcRec::Proc proc = rec.chooseProc(&bltr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 // we have to back up subsequent passes if we're in polygon mode
598 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 do {
601 size_t n = count;
602 if (n > MAX_DEV_PTS) {
603 n = MAX_DEV_PTS;
604 }
605 matrix->mapPoints(devPts, pts, n);
606 proc(rec, devPts, n, bltr);
607 pts += n - backup;
608 SkASSERT(count >= n);
609 count -= n;
610 if (count > 0) {
611 count += backup;
612 }
613 } while (count != 0);
614 } else {
615 switch (mode) {
616 case SkCanvas::kPoints_PointMode: {
617 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000618 SkPaint newPaint(paint);
619 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620
reed@google.com40c2ba22011-07-25 19:41:22 +0000621 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000623
reed@google.com40c2ba22011-07-25 19:41:22 +0000624 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 SkPath path;
626 SkMatrix preMatrix;
reed@google.coma76de3d2011-01-13 18:30:42 +0000627
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 path.addCircle(0, 0, radius);
629 for (size_t i = 0; i < count; i++) {
630 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
631 // pass true for the last point, since we can modify
632 // then path then
reed@android.comf2b98d62010-12-20 18:26:13 +0000633 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000634 fDevice->drawPath(*this, path, newPaint, &preMatrix,
reed@android.comf2b98d62010-12-20 18:26:13 +0000635 (count-1) == i);
636 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000637 this->drawPath(path, newPaint, &preMatrix,
638 (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000639 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 }
641 } else {
642 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000643
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 for (size_t i = 0; i < count; i++) {
645 r.fLeft = pts[i].fX - radius;
646 r.fTop = pts[i].fY - radius;
647 r.fRight = r.fLeft + width;
648 r.fBottom = r.fTop + width;
reed@android.comf2b98d62010-12-20 18:26:13 +0000649 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000650 fDevice->drawRect(*this, r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000651 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000652 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000653 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 }
655 }
656 break;
657 }
658 case SkCanvas::kLines_PointMode:
robertphillips@google.com629ab542012-11-28 17:18:11 +0000659#ifndef SK_DISABLE_DASHING_OPTIMIZATION
660 if (2 == count && NULL != paint.getPathEffect()) {
661 // most likely a dashed line - see if it is one of the ones
662 // we can accelerate
663 SkStrokeRec rec(paint);
664 SkPathEffect::PointData dst;
665
666 SkPath path;
667 path.moveTo(pts[0]);
668 path.lineTo(pts[1]);
669
670 if (paint.getPathEffect()->asPoints(&dst, path, rec, *fMatrix) &&
671 SK_Scalar1 == dst.fSize.fX && SK_Scalar1 == dst.fSize.fY &&
672 !(SkPathEffect::PointData::kUsePath_PointFlag & dst.fFlags)) {
673 SkPaint newP(paint);
674 newP.setPathEffect(NULL);
robertphillips@google.com935ad022012-12-05 19:07:21 +0000675 newP.setStyle(SkPaint::kFill_Style);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000676
677 if (SkPathEffect::PointData::kCircles_PointFlag & dst.fFlags) {
678 newP.setStrokeCap(SkPaint::kRound_Cap);
679 } else {
680 newP.setStrokeCap(SkPaint::kButt_Cap);
681 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000682 if (fDevice) {
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +0000683 fDevice->drawPoints(*this,
robertphillips@google.com935ad022012-12-05 19:07:21 +0000684 SkCanvas::kPoints_PointMode,
685 dst.fNumPoints,
686 dst.fPoints,
687 newP);
688 } else {
689 this->drawPoints(SkCanvas::kPoints_PointMode,
690 dst.fNumPoints,
691 dst.fPoints,
692 newP,
693 forceUseDevice);
694 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000695 break;
696 }
697 }
698#endif // DISABLE_DASHING_OPTIMIZATION
699 // couldn't take fast path so fall through!
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 case SkCanvas::kPolygon_PointMode: {
701 count -= 1;
702 SkPath path;
703 SkPaint p(paint);
704 p.setStyle(SkPaint::kStroke_Style);
705 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
706 for (size_t i = 0; i < count; i += inc) {
707 path.moveTo(pts[i]);
708 path.lineTo(pts[i+1]);
reed@android.comf2b98d62010-12-20 18:26:13 +0000709 if (fDevice) {
710 fDevice->drawPath(*this, path, p, NULL, true);
711 } else {
712 this->drawPath(path, p, NULL, true);
713 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714 path.rewind();
715 }
716 break;
717 }
718 }
719 }
720}
721
722static inline SkPoint* as_lefttop(SkRect* r) {
723 return (SkPoint*)(void*)r;
724}
725
726static inline SkPoint* as_rightbottom(SkRect* r) {
727 return ((SkPoint*)(void*)r) + 1;
728}
729
reed@google.com761fb622011-04-04 18:58:05 +0000730static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
731 SkPoint* strokeSize) {
732 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
733 paint.getStrokeMiter() < SK_ScalarSqrt2) {
734 return false;
735 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000736
reed@google.com761fb622011-04-04 18:58:05 +0000737 SkASSERT(matrix.rectStaysRect());
738 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
739 matrix.mapVectors(strokeSize, &pt, 1);
reed@google.com61153382011-04-05 13:05:18 +0000740 strokeSize->fX = SkScalarAbs(strokeSize->fX);
741 strokeSize->fY = SkScalarAbs(strokeSize->fY);
reed@google.com761fb622011-04-04 18:58:05 +0000742 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000743}
744
reed@google.com62ab7ad2011-04-05 14:08:25 +0000745SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
746 const SkMatrix& matrix,
747 SkPoint* strokeSize) {
748 RectType rtype;
749 const SkScalar width = paint.getStrokeWidth();
750 const bool zeroWidth = (0 == width);
751 SkPaint::Style style = paint.getStyle();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000752
reed@google.com62ab7ad2011-04-05 14:08:25 +0000753 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
754 style = SkPaint::kFill_Style;
755 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000756
reed@google.com62ab7ad2011-04-05 14:08:25 +0000757 if (paint.getPathEffect() || paint.getMaskFilter() ||
758 paint.getRasterizer() || !matrix.rectStaysRect() ||
759 SkPaint::kStrokeAndFill_Style == style) {
760 rtype = kPath_RectType;
761 } else if (SkPaint::kFill_Style == style) {
762 rtype = kFill_RectType;
763 } else if (zeroWidth) {
764 rtype = kHair_RectType;
765 } else if (easy_rect_join(paint, matrix, strokeSize)) {
766 rtype = kStroke_RectType;
767 } else {
768 rtype = kPath_RectType;
769 }
770 return rtype;
771}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000772
reed@google.com73244152012-05-11 14:35:23 +0000773static const SkPoint* rect_points(const SkRect& r) {
774 return (const SkPoint*)(void*)&r;
775}
776
777static SkPoint* rect_points(SkRect& r) {
778 return (SkPoint*)(void*)&r;
reed@google.com40c2ba22011-07-25 19:41:22 +0000779}
780
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000782 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783
784 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000785 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 return;
787 }
788
reed@google.com761fb622011-04-04 18:58:05 +0000789 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000790 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000791
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000792 if (kPath_RectType == rtype) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 SkPath tmp;
794 tmp.addRect(rect);
795 tmp.setFillType(SkPath::kWinding_FillType);
reed@android.com187d5592009-07-08 14:03:56 +0000796 this->drawPath(tmp, paint, NULL, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 return;
798 }
799
800 const SkMatrix& matrix = *fMatrix;
801 SkRect devRect;
802
803 // transform rect into devRect
reed@google.com73244152012-05-11 14:35:23 +0000804 matrix.mapPoints(rect_points(devRect), rect_points(rect), 2);
805 devRect.sort();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806
807 if (fBounder && !fBounder->doRect(devRect, paint)) {
808 return;
809 }
810
811 // look for the quick exit, before we build a blitter
reed@google.com73244152012-05-11 14:35:23 +0000812 if (true) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 SkIRect ir;
814 devRect.roundOut(&ir);
reed@android.com55e76b22009-11-23 21:46:47 +0000815 if (paint.getStyle() != SkPaint::kFill_Style) {
816 // extra space for hairlines
817 ir.inset(-1, -1);
818 }
reed@google.com045e62d2011-10-24 12:19:46 +0000819 if (fRC->quickReject(ir))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 return;
821 }
822
823 SkAutoBlitterChoose blitterStorage(*fBitmap, matrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000824 const SkRasterClip& clip = *fRC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 SkBlitter* blitter = blitterStorage.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826
reed@android.comb641c9f22010-03-25 14:31:46 +0000827 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
828 // case we are also hairline (if we've gotten to here), which devolves to
829 // effectively just kFill
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000830 switch (rtype) {
831 case kFill_RectType:
832 if (paint.isAntiAlias()) {
833 SkScan::AntiFillRect(devRect, clip, blitter);
834 } else {
835 SkScan::FillRect(devRect, clip, blitter);
836 }
837 break;
838 case kStroke_RectType:
839 if (paint.isAntiAlias()) {
reed@google.com761fb622011-04-04 18:58:05 +0000840 SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000841 } else {
reed@google.com761fb622011-04-04 18:58:05 +0000842 SkScan::FrameRect(devRect, strokeSize, clip, blitter);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000843 }
844 break;
845 case kHair_RectType:
846 if (paint.isAntiAlias()) {
847 SkScan::AntiHairRect(devRect, clip, blitter);
848 } else {
849 SkScan::HairRect(devRect, clip, blitter);
850 }
851 break;
852 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000853 SkDEBUGFAIL("bad rtype");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 }
855}
856
857void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
858 if (srcM.fBounds.isEmpty()) {
859 return;
860 }
861
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000862 const SkMask* mask = &srcM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000864 SkMask dstM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 if (paint.getMaskFilter() &&
866 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) {
867 mask = &dstM;
bungeman@google.combf2ac7e2011-09-26 19:51:33 +0000868 } else {
869 dstM.fImage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000871 SkAutoMaskFreeImage ami(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872
873 if (fBounder && !fBounder->doIRect(mask->fBounds)) {
874 return;
875 }
876
reed@google.com045e62d2011-10-24 12:19:46 +0000877 SkAutoBlitterChoose blitterChooser(*fBitmap, *fMatrix, paint);
878 SkBlitter* blitter = blitterChooser.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879
reed@google.com045e62d2011-10-24 12:19:46 +0000880 SkAAClipBlitterWrapper wrapper;
881 const SkRegion* clipRgn;
882
883 if (fRC->isBW()) {
884 clipRgn = &fRC->bwRgn();
885 } else {
886 wrapper.init(*fRC, blitter);
887 clipRgn = &wrapper.getRgn();
888 blitter = wrapper.getBlitter();
889 }
890 blitter->blitMaskRegion(*mask, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891}
892
reed@android.comebdeeb82009-09-03 21:45:49 +0000893static SkScalar fast_len(const SkVector& vec) {
894 SkScalar x = SkScalarAbs(vec.fX);
895 SkScalar y = SkScalarAbs(vec.fY);
896 if (x < y) {
897 SkTSwap(x, y);
898 }
899 return x + SkScalarHalf(y);
900}
901
reed@google.comecadf992011-09-19 19:18:18 +0000902static bool xfermodeSupportsCoverageAsAlpha(SkXfermode* xfer) {
903 SkXfermode::Coeff dc;
904 if (!SkXfermode::AsCoeff(xfer, NULL, &dc)) {
905 return false;
906 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000907
reed@google.comecadf992011-09-19 19:18:18 +0000908 switch (dc) {
909 case SkXfermode::kOne_Coeff:
910 case SkXfermode::kISA_Coeff:
911 case SkXfermode::kISC_Coeff:
912 return true;
913 default:
914 return false;
915 }
916}
917
918bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
bsalomon@google.comdd1be602012-01-18 20:34:00 +0000919 SkScalar* coverage) {
920 SkASSERT(coverage);
reed@google.comecadf992011-09-19 19:18:18 +0000921 if (SkPaint::kStroke_Style != paint.getStyle()) {
922 return false;
923 }
924 SkScalar strokeWidth = paint.getStrokeWidth();
925 if (0 == strokeWidth) {
bsalomon@google.comdd1be602012-01-18 20:34:00 +0000926 *coverage = SK_Scalar1;
reed@google.comecadf992011-09-19 19:18:18 +0000927 return true;
928 }
929
930 // if we get here, we need to try to fake a thick-stroke with a modulated
931 // hairline
932
933 if (!paint.isAntiAlias()) {
934 return false;
935 }
tomhudson@google.com8d430182011-06-06 19:11:19 +0000936 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000937 return false;
938 }
reed@google.comecadf992011-09-19 19:18:18 +0000939
reed@android.comebdeeb82009-09-03 21:45:49 +0000940 SkVector src[2], dst[2];
reed@google.comecadf992011-09-19 19:18:18 +0000941 src[0].set(strokeWidth, 0);
942 src[1].set(0, strokeWidth);
reed@android.comebdeeb82009-09-03 21:45:49 +0000943 matrix.mapVectors(dst, src, 2);
944 SkScalar len0 = fast_len(dst[0]);
945 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000946 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
bsalomon@google.comdd1be602012-01-18 20:34:00 +0000947 *coverage = SkScalarAve(len0, len1);
reed@android.comebdeeb82009-09-03 21:45:49 +0000948 return true;
949 }
950 return false;
951}
952
reed@google.com32e5d972011-07-27 18:25:57 +0000953void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 const SkMatrix* prePathMatrix, bool pathIsMutable) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000955 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956
957 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000958 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 return;
960 }
961
962 SkPath* pathPtr = (SkPath*)&origSrcPath;
963 bool doFill = true;
964 SkPath tmpPath;
965 SkMatrix tmpMatrix;
966 const SkMatrix* matrix = fMatrix;
967
968 if (prePathMatrix) {
reed@google.com32e5d972011-07-27 18:25:57 +0000969 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
970 origPaint.getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +0000972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 if (!pathIsMutable) {
974 result = &tmpPath;
975 pathIsMutable = true;
976 }
977 pathPtr->transform(*prePathMatrix, result);
978 pathPtr = result;
979 } else {
980 if (!tmpMatrix.setConcat(*matrix, *prePathMatrix)) {
981 // overflow
982 return;
983 }
984 matrix = &tmpMatrix;
985 }
986 }
987 // at this point we're done with prePathMatrix
988 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +0000989
bsalomon@google.com5dc26b92012-10-11 19:32:32 +0000990 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
reed@google.coma76de3d2011-01-13 18:30:42 +0000991
reed@google.comecadf992011-09-19 19:18:18 +0000992 {
bsalomon@google.comdd1be602012-01-18 20:34:00 +0000993 SkScalar coverage;
994 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
995 if (SK_Scalar1 == coverage) {
bsalomon@google.com5dc26b92012-10-11 19:32:32 +0000996 paint.writable()->setStrokeWidth(0);
bsalomon@google.comdd1be602012-01-18 20:34:00 +0000997 } else if (xfermodeSupportsCoverageAsAlpha(origPaint.getXfermode())) {
998 U8CPU newAlpha;
999#if 0
1000 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
1001 origPaint.getAlpha()));
1002#else
1003 // this is the old technique, which we preserve for now so
1004 // we don't change previous results (testing)
1005 // the new way seems fine, its just (a tiny bit) different
1006 int scale = (int)SkScalarMul(coverage, 256);
1007 newAlpha = origPaint.getAlpha() * scale >> 8;
1008#endif
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001009 SkPaint* writablePaint = paint.writable();
1010 writablePaint->setStrokeWidth(0);
1011 writablePaint->setAlpha(newAlpha);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001012 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 }
1014 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001015
reed@google.com32e5d972011-07-27 18:25:57 +00001016 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
1017 doFill = paint->getFillPath(*pathPtr, &tmpPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 pathPtr = &tmpPath;
1019 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001020
reed@google.com32e5d972011-07-27 18:25:57 +00001021 if (paint->getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 SkMask mask;
reed@google.com32e5d972011-07-27 18:25:57 +00001023 if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
reed@google.com045e62d2011-10-24 12:19:46 +00001024 &fRC->getBounds(), paint->getMaskFilter(), &mask,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
reed@google.com32e5d972011-07-27 18:25:57 +00001026 this->drawDevMask(mask, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 SkMask::FreeImage(mask.fImage);
1028 }
1029 return;
1030 }
1031
1032 // avoid possibly allocating a new path in transform if we can
1033 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1034
1035 // transform the path into device space
1036 pathPtr->transform(*matrix, devPathPtr);
1037
reed@google.com32e5d972011-07-27 18:25:57 +00001038 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001040 if (paint->getMaskFilter()) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001041 SkPaint::Style style = doFill ? SkPaint::kFill_Style :
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001042 SkPaint::kStroke_Style;
1043 if (paint->getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fRC,
1044 fBounder, blitter.get(),
1045 style)) {
1046 return; // filterPath() called the blitter, so we're done
1047 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 }
1049
reed@google.com32e5d972011-07-27 18:25:57 +00001050 if (fBounder && !fBounder->doPath(*devPathPtr, *paint, doFill)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 return;
1052 }
1053
reed@google.com045e62d2011-10-24 12:19:46 +00001054 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 if (doFill) {
reed@google.com32e5d972011-07-27 18:25:57 +00001056 if (paint->isAntiAlias()) {
reed@google.com045e62d2011-10-24 12:19:46 +00001057 proc = SkScan::AntiFillPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 } else {
reed@google.com045e62d2011-10-24 12:19:46 +00001059 proc = SkScan::FillPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 }
1061 } else { // hairline
reed@google.com32e5d972011-07-27 18:25:57 +00001062 if (paint->isAntiAlias()) {
reed@google.com045e62d2011-10-24 12:19:46 +00001063 proc = SkScan::AntiHairPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 } else {
reed@google.com045e62d2011-10-24 12:19:46 +00001065 proc = SkScan::HairPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 }
1067 }
reed@google.com045e62d2011-10-24 12:19:46 +00001068 proc(*devPathPtr, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069}
1070
reed@android.com0baf1932009-06-24 12:41:42 +00001071/** For the purposes of drawing bitmaps, if a matrix is "almost" translate
1072 go ahead and treat it as if it were, so that subsequent code can go fast.
1073 */
1074static bool just_translate(const SkMatrix& matrix, const SkBitmap& bitmap) {
1075 SkMatrix::TypeMask mask = matrix.getType();
1076
1077 if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
1078 return false;
1079 }
1080 if (mask & SkMatrix::kScale_Mask) {
1081 SkScalar sx = matrix[SkMatrix::kMScaleX];
1082 SkScalar sy = matrix[SkMatrix::kMScaleY];
1083 int w = bitmap.width();
1084 int h = bitmap.height();
1085 int sw = SkScalarRound(SkScalarMul(sx, SkIntToScalar(w)));
1086 int sh = SkScalarRound(SkScalarMul(sy, SkIntToScalar(h)));
1087 return sw == w && sh == h;
1088 }
1089 // if we got here, we're either kTranslate_Mask or identity
1090 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091}
1092
1093void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
1094 const SkPaint& paint) const {
1095 SkASSERT(bitmap.getConfig() == SkBitmap::kA8_Config);
1096
reed@google.coma76de3d2011-01-13 18:30:42 +00001097 if (just_translate(*fMatrix, bitmap)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 int ix = SkScalarRound(fMatrix->getTranslateX());
1099 int iy = SkScalarRound(fMatrix->getTranslateY());
1100
reed@google.coma641f3f2012-12-13 22:16:30 +00001101 SkAutoLockPixels alp(bitmap);
1102 if (!bitmap.readyToDraw()) {
1103 return;
1104 }
1105
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 SkMask mask;
1107 mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
1108 mask.fFormat = SkMask::kA8_Format;
1109 mask.fRowBytes = bitmap.rowBytes();
1110 mask.fImage = bitmap.getAddr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001111
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 this->drawDevMask(mask, paint);
1113 } else { // need to xform the bitmap first
1114 SkRect r;
1115 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 r.set(0, 0,
1118 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1119 fMatrix->mapRect(&r);
1120 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001121
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 // set the mask's bounds to the transformed bitmap-bounds,
1123 // clipped to the actual device
1124 {
1125 SkIRect devBounds;
1126 devBounds.set(0, 0, fBitmap->width(), fBitmap->height());
1127 // need intersect(l, t, r, b) on irect
1128 if (!mask.fBounds.intersect(devBounds)) {
1129 return;
1130 }
1131 }
reed@android.com543ed932009-04-24 12:43:40 +00001132
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 mask.fFormat = SkMask::kA8_Format;
1134 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001135 size_t size = mask.computeImageSize();
1136 if (0 == size) {
1137 // the mask is too big to allocated, draw nothing
1138 return;
1139 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140
1141 // allocate (and clear) our temp buffer to hold the transformed bitmap
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 SkAutoMalloc storage(size);
1143 mask.fImage = (uint8_t*)storage.get();
1144 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001145
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1147 {
1148 SkBitmap device;
1149 device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
1150 mask.fBounds.height(), mask.fRowBytes);
1151 device.setPixels(mask.fImage);
reed@google.coma76de3d2011-01-13 18:30:42 +00001152
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 SkCanvas c(device);
1154 // need the unclipped top/left for the translate
1155 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1156 -SkIntToScalar(mask.fBounds.fTop));
1157 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001158
1159 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001160 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001161 SkPaint tmpPaint;
1162 tmpPaint.setFlags(paint.getFlags());
reed@google.com40c2ba22011-07-25 19:41:22 +00001163 SkAutoBitmapShaderInstall install(bitmap, tmpPaint);
reed@android.com3469c762009-02-24 19:03:20 +00001164 SkRect rr;
1165 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1166 SkIntToScalar(bitmap.height()));
reed@google.com40c2ba22011-07-25 19:41:22 +00001167 c.drawRect(rr, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168 }
1169 this->drawDevMask(mask, paint);
1170 }
1171}
1172
reed@google.com045e62d2011-10-24 12:19:46 +00001173static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 const SkRect& srcR) {
1175 SkRect dstR;
1176 SkIRect devIR;
reed@google.coma76de3d2011-01-13 18:30:42 +00001177
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 m.mapRect(&dstR, srcR);
reed@google.coma76de3d2011-01-13 18:30:42 +00001179 dstR.roundOut(&devIR);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 return c.quickReject(devIR);
1181}
1182
reed@google.com045e62d2011-10-24 12:19:46 +00001183static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 int width, int height) {
1185 SkRect r;
1186 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1187 return clipped_out(matrix, clip, r);
1188}
1189
reed@google.com045e62d2011-10-24 12:19:46 +00001190static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y,
1191 const SkBitmap& bitmap) {
1192 return clip.isBW() ||
1193 clip.quickContains(x, y, x + bitmap.width(), y + bitmap.height());
1194}
1195
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed@google.com40c2ba22011-07-25 19:41:22 +00001197 const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001198 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199
1200 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001201 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001203 bitmap.getConfig() == SkBitmap::kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 return;
1205 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001206
1207#ifndef SK_ALLOW_OVER_32K_BITMAPS
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 // run away on too-big bitmaps for now (exceed 16.16)
1209 if (bitmap.width() > 32767 || bitmap.height() > 32767) {
1210 return;
1211 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001212#endif
1213
reed@google.com40c2ba22011-07-25 19:41:22 +00001214 SkPaint paint(origPaint);
1215 paint.setStyle(SkPaint::kFill_Style);
reed@google.coma76de3d2011-01-13 18:30:42 +00001216
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 SkMatrix matrix;
1218 if (!matrix.setConcat(*fMatrix, prematrix)) {
1219 return;
1220 }
1221
reed@google.com045e62d2011-10-24 12:19:46 +00001222 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 return;
1224 }
1225
reed@android.com218521e2010-02-09 13:06:29 +00001226 if (fBounder && just_translate(matrix, bitmap)) {
1227 SkIRect ir;
1228 int32_t ix = SkScalarRound(matrix.getTranslateX());
1229 int32_t iy = SkScalarRound(matrix.getTranslateY());
1230 ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
1231 if (!fBounder->doIRect(ir)) {
1232 return;
1233 }
1234 }
1235
reed@android.com0baf1932009-06-24 12:41:42 +00001236 if (bitmap.getConfig() != SkBitmap::kA8_Config &&
1237 just_translate(matrix, bitmap)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001238 //
1239 // It is safe to call lock pixels now, since we know the matrix is
1240 // (more or less) identity.
1241 //
1242 SkAutoLockPixels alp(bitmap);
1243 if (!bitmap.readyToDraw()) {
1244 return;
1245 }
reed@google.com045e62d2011-10-24 12:19:46 +00001246 int ix = SkScalarRound(matrix.getTranslateX());
1247 int iy = SkScalarRound(matrix.getTranslateY());
1248 if (clipHandlesSprite(*fRC, ix, iy, bitmap)) {
1249 uint32_t storage[kBlitterStorageLongCount];
1250 SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
1251 ix, iy, storage, sizeof(storage));
1252 if (blitter) {
1253 SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254
reed@google.com045e62d2011-10-24 12:19:46 +00001255 SkIRect ir;
1256 ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257
reed@google.com045e62d2011-10-24 12:19:46 +00001258 SkScan::FillIRect(ir, *fRC, blitter);
1259 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001263
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 // now make a temp draw on the stack, and use it
1265 //
1266 SkDraw draw(*this);
1267 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 if (bitmap.getConfig() == SkBitmap::kA8_Config) {
1270 draw.drawBitmapAsMask(bitmap, paint);
1271 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +00001272 SkAutoBitmapShaderInstall install(bitmap, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273
1274 SkRect r;
1275 r.set(0, 0, SkIntToScalar(bitmap.width()),
1276 SkIntToScalar(bitmap.height()));
reed@google.coma76de3d2011-01-13 18:30:42 +00001277 // is this ok if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001278 draw.drawRect(r, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 }
1280}
1281
1282void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y,
reed@google.com40c2ba22011-07-25 19:41:22 +00001283 const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001284 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001287 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001289 bitmap.getConfig() == SkBitmap::kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 return;
1291 }
1292
1293 SkIRect bounds;
1294 bounds.set(x, y, x + bitmap.width(), y + bitmap.height());
1295
reed@google.com045e62d2011-10-24 12:19:46 +00001296 if (fRC->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 return; // nothing to draw
1298 }
1299
reed@google.com40c2ba22011-07-25 19:41:22 +00001300 SkPaint paint(origPaint);
1301 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302
reed@google.com045e62d2011-10-24 12:19:46 +00001303 if (NULL == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, bitmap)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 uint32_t storage[kBlitterStorageLongCount];
1305 SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
1306 x, y, storage, sizeof(storage));
1307
1308 if (blitter) {
1309 SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
1310
1311 if (fBounder && !fBounder->doIRect(bounds)) {
1312 return;
1313 }
1314
reed@google.com045e62d2011-10-24 12:19:46 +00001315 SkScan::FillIRect(bounds, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316 return;
1317 }
1318 }
1319
reed@google.com40c2ba22011-07-25 19:41:22 +00001320 SkAutoBitmapShaderInstall install(bitmap, paint);
1321 const SkPaint& shaderPaint = install.paintWithShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322
1323 SkMatrix matrix;
1324 SkRect r;
1325
1326 // get a scalar version of our rect
1327 r.set(bounds);
1328
1329 // tell the shader our offset
1330 matrix.setTranslate(r.fLeft, r.fTop);
reed@google.com40c2ba22011-07-25 19:41:22 +00001331 shaderPaint.getShader()->setLocalMatrix(matrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00001332
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 SkDraw draw(*this);
1334 matrix.reset();
1335 draw.fMatrix = &matrix;
1336 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001337 // is this OK if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001338 draw.drawRect(r, shaderPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339}
1340
1341///////////////////////////////////////////////////////////////////////////////
1342
1343#include "SkScalerContext.h"
1344#include "SkGlyphCache.h"
reed@google.come6913762012-08-07 15:19:47 +00001345#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346#include "SkUtils.h"
1347
1348static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
1349 const char text[], size_t byteLength, SkVector* stopVector) {
1350 SkFixed x = 0, y = 0;
1351 const char* stop = text + byteLength;
1352
1353 SkAutoKern autokern;
reed@google.coma76de3d2011-01-13 18:30:42 +00001354
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 while (text < stop) {
1356 // don't need x, y here, since all subpixel variants will have the
1357 // same advance
1358 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1359
1360 x += autokern.adjust(glyph) + glyph.fAdvanceX;
1361 y += glyph.fAdvanceY;
1362 }
1363 stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
1364
1365 SkASSERT(text == stop);
1366}
1367
1368void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
1369 SkScalar x, SkScalar y,
1370 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001371 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372
djsollen@google.com166e6532012-03-20 14:24:38 +00001373 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374
1375 SkMatrix matrix;
1376 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1377 matrix.postTranslate(x, y);
1378
1379 const SkPath* iterPath;
1380 SkScalar xpos, prevXPos = 0;
1381
reed@google.com7b4531f2012-08-07 15:53:00 +00001382 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001384 if (iterPath) {
1385 const SkPaint& pnt = iter.getPaint();
1386 if (fDevice) {
1387 fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
1388 } else {
1389 this->drawPath(*iterPath, pnt, &matrix, false);
1390 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001391 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392 prevXPos = xpos;
1393 }
1394}
1395
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396// disable warning : local variable used without having been initialized
reed@google.coma76de3d2011-01-13 18:30:42 +00001397#if defined _WIN32 && _MSC_VER >= 1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398#pragma warning ( push )
1399#pragma warning ( disable : 4701 )
1400#endif
1401
1402//////////////////////////////////////////////////////////////////////////////
1403
1404static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001405 SkFixed fx, SkFixed fy,
1406 const SkGlyph& glyph) {
1407 int left = SkFixedFloor(fx);
1408 int top = SkFixedFloor(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001410 SkASSERT(NULL == state.fBounder);
1411 SkASSERT((NULL == state.fClip && state.fAAClip) ||
1412 (state.fClip && NULL == state.fAAClip && state.fClip->isRect()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413
1414 left += glyph.fLeft;
1415 top += glyph.fTop;
1416
1417 int right = left + glyph.fWidth;
1418 int bottom = top + glyph.fHeight;
1419
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001420 SkMask mask;
1421 SkIRect storage;
1422 SkIRect* bounds = &mask.fBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423
tomhudson@google.com83a44462011-10-27 15:27:51 +00001424 mask.fBounds.set(left, top, right, bottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425
tomhudson@google.com83a44462011-10-27 15:27:51 +00001426 // this extra test is worth it, assuming that most of the time it succeeds
1427 // since we can avoid writing to storage
1428 if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) {
1429 if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds))
1430 return;
1431 bounds = &storage;
1432 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001433
tomhudson@google.com83a44462011-10-27 15:27:51 +00001434 uint8_t* aa = (uint8_t*)glyph.fImage;
1435 if (NULL == aa) {
1436 aa = (uint8_t*)state.fCache->findImage(glyph);
1437 if (NULL == aa) {
1438 return; // can't rasterize glyph
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 }
tomhudson@google.com83a44462011-10-27 15:27:51 +00001440 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441
tomhudson@google.com83a44462011-10-27 15:27:51 +00001442 mask.fRowBytes = glyph.rowBytes();
1443 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1444 mask.fImage = aa;
1445 state.fBlitter->blitMask(mask, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446}
1447
1448static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001449 SkFixed fx, SkFixed fy,
tomhudson@google.com83a44462011-10-27 15:27:51 +00001450 const SkGlyph& glyph) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001451 int left = SkFixedFloor(fx);
1452 int top = SkFixedFloor(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001454 SkASSERT(!state.fClip->isRect());
1455 SkASSERT(NULL == state.fBounder);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456
1457 SkMask mask;
1458
1459 left += glyph.fLeft;
1460 top += glyph.fTop;
1461
1462 mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001463 SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464
tomhudson@google.com83a44462011-10-27 15:27:51 +00001465 if (!clipper.done()) {
1466 const SkIRect& cr = clipper.rect();
1467 const uint8_t* aa = (const uint8_t*)glyph.fImage;
1468 if (NULL == aa) {
1469 aa = (uint8_t*)state.fCache->findImage(glyph);
1470 if (NULL == aa) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001471 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472 }
tomhudson@google.com83a44462011-10-27 15:27:51 +00001473 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001474
tomhudson@google.com83a44462011-10-27 15:27:51 +00001475 mask.fRowBytes = glyph.rowBytes();
1476 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1477 mask.fImage = (uint8_t*)aa;
1478 do {
1479 state.fBlitter->blitMask(mask, cr);
1480 clipper.next();
1481 } while (!clipper.done());
1482 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483}
1484
1485static void D1G_Bounder(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001486 SkFixed fx, SkFixed fy,
tomhudson@google.com83a44462011-10-27 15:27:51 +00001487 const SkGlyph& glyph) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001488 int left = SkFixedFloor(fx);
1489 int top = SkFixedFloor(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001491
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492 SkMask mask;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001493
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 left += glyph.fLeft;
1495 top += glyph.fTop;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001496
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
1498 SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001499
tomhudson@google.com83a44462011-10-27 15:27:51 +00001500 if (!clipper.done()) {
1501 const SkIRect& cr = clipper.rect();
1502 const uint8_t* aa = (const uint8_t*)glyph.fImage;
1503 if (NULL == aa) {
1504 aa = (uint8_t*)state.fCache->findImage(glyph);
1505 if (NULL == aa) {
1506 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001508 }
1509
reed@android.comd055c1f2010-03-01 14:54:05 +00001510 // we need to pass the origin, which we approximate with our
1511 // (unadjusted) left,top coordinates (the caller called fixedfloor)
tomhudson@google.com83a44462011-10-27 15:27:51 +00001512 if (state.fBounder->doIRectGlyph(cr,
reed@android.comd055c1f2010-03-01 14:54:05 +00001513 left - glyph.fLeft,
1514 top - glyph.fTop, glyph)) {
tomhudson@google.com83a44462011-10-27 15:27:51 +00001515 mask.fRowBytes = glyph.rowBytes();
1516 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1517 mask.fImage = (uint8_t*)aa;
1518 do {
1519 state.fBlitter->blitMask(mask, cr);
1520 clipper.next();
1521 } while (!clipper.done());
1522 }
1523 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524}
1525
reed@google.com045e62d2011-10-24 12:19:46 +00001526static void D1G_Bounder_AAClip(const SkDraw1Glyph& state,
1527 SkFixed fx, SkFixed fy,
1528 const SkGlyph& glyph) {
1529 int left = SkFixedFloor(fx);
1530 int top = SkFixedFloor(fy);
1531 SkIRect bounds;
1532 bounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
1533
1534 if (state.fBounder->doIRectGlyph(bounds, left, top, glyph)) {
1535 D1G_NoBounder_RectClip(state, fx, fy, glyph);
1536 }
1537}
1538
reed@google.comfd4236e2011-07-25 21:16:22 +00001539static bool hasCustomD1GProc(const SkDraw& draw) {
1540 return draw.fProcs && draw.fProcs->fD1GProc;
1541}
1542
1543static bool needsRasterTextBlit(const SkDraw& draw) {
1544 return !hasCustomD1GProc(draw);
1545}
1546
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
1548 SkGlyphCache* cache) {
1549 fDraw = draw;
bungeman@google.com2211b622012-01-13 15:02:58 +00001550 fBounder = draw->fBounder;
1551 fBlitter = blitter;
1552 fCache = cache;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001554 if (hasCustomD1GProc(*draw)) {
reed@google.com045e62d2011-10-24 12:19:46 +00001555 // todo: fix this assumption about clips w/ custom
1556 fClip = draw->fClip;
1557 fClipBounds = fClip->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558 return draw->fProcs->fD1GProc;
1559 }
1560
reed@google.com045e62d2011-10-24 12:19:46 +00001561 if (draw->fRC->isBW()) {
1562 fAAClip = NULL;
1563 fClip = &draw->fRC->bwRgn();
1564 fClipBounds = fClip->getBounds();
1565 if (NULL == fBounder) {
1566 if (fClip->isRect()) {
1567 return D1G_NoBounder_RectClip;
1568 } else {
1569 return D1G_NoBounder_RgnClip;
1570 }
1571 } else {
1572 return D1G_Bounder;
1573 }
1574 } else { // aaclip
1575 fAAClip = &draw->fRC->aaRgn();
1576 fClip = NULL;
1577 fClipBounds = fAAClip->getBounds();
1578 if (NULL == fBounder) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 return D1G_NoBounder_RectClip;
1580 } else {
reed@google.com045e62d2011-10-24 12:19:46 +00001581 return D1G_Bounder_AAClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 }
1584}
1585
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586///////////////////////////////////////////////////////////////////////////////
1587
1588void SkDraw::drawText(const char text[], size_t byteLength,
1589 SkScalar x, SkScalar y, const SkPaint& paint) const {
1590 SkASSERT(byteLength == 0 || text != NULL);
1591
reed@android.comf2b98d62010-12-20 18:26:13 +00001592 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593
1594 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001595 if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596 return;
1597 }
1598
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001599 // SkScalarRec doesn't currently have a way of representing hairline stroke and
1600 // will fill if its frame-width is 0.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601 if (/*paint.isLinearText() ||*/
skia.committer@gmail.comfc843592012-10-11 02:01:14 +00001602 (fMatrix->hasPerspective()) ||
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001603 (0 == paint.getStrokeWidth() && SkPaint::kStroke_Style == paint.getStyle())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 this->drawText_asPaths(text, byteLength, x, y, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 return;
1606 }
1607
1608 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
1609
reed@android.comf2b98d62010-12-20 18:26:13 +00001610 const SkMatrix* matrix = fMatrix;
reed@android.comf2b98d62010-12-20 18:26:13 +00001611
1612 SkAutoGlyphCache autoCache(paint, matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 SkGlyphCache* cache = autoCache.getCache();
reed@google.coma76de3d2011-01-13 18:30:42 +00001614
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 // transform our starting point
1616 {
1617 SkPoint loc;
reed@android.comf2b98d62010-12-20 18:26:13 +00001618 matrix->mapXY(x, y, &loc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 x = loc.fX;
1620 y = loc.fY;
1621 }
1622
1623 // need to measure first
1624 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
1625 SkVector stop;
1626
1627 measure_text(cache, glyphCacheProc, text, byteLength, &stop);
1628
1629 SkScalar stopX = stop.fX;
1630 SkScalar stopY = stop.fY;
1631
1632 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1633 stopX = SkScalarHalf(stopX);
1634 stopY = SkScalarHalf(stopY);
1635 }
1636 x -= stopX;
1637 y -= stopY;
1638 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001639
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640 SkFixed fx = SkScalarToFixed(x);
1641 SkFixed fy = SkScalarToFixed(y);
1642 const char* stop = text + byteLength;
1643
reed@android.comf2b98d62010-12-20 18:26:13 +00001644 SkFixed fxMask = ~0;
1645 SkFixed fyMask = ~0;
bungeman@google.com2211b622012-01-13 15:02:58 +00001646 if (cache->isSubpixel()) {
reed@google.comcb6ccdd2011-08-23 21:30:47 +00001647 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*matrix);
1648 if (kX_SkAxisAlignment == baseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001649 fyMask = 0;
reed@google.comcb6ccdd2011-08-23 21:30:47 +00001650 } else if (kY_SkAxisAlignment == baseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001651 fxMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001653
bungeman@google.com2211b622012-01-13 15:02:58 +00001654 // apply bias here to avoid adding 1/2 the sampling frequency in the loop
1655 fx += SK_FixedHalf >> SkGlyph::kSubBits;
1656 fy += SK_FixedHalf >> SkGlyph::kSubBits;
1657 } else {
1658 fx += SK_FixedHalf;
1659 fy += SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660 }
1661
reed@google.com045e62d2011-10-24 12:19:46 +00001662 SkAAClipBlitter aaBlitter;
1663 SkAutoBlitterChoose blitterChooser;
1664 SkBlitter* blitter = NULL;
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001665 if (needsRasterTextBlit(*this)) {
reed@google.com045e62d2011-10-24 12:19:46 +00001666 blitterChooser.choose(*fBitmap, *matrix, paint);
1667 blitter = blitterChooser.get();
1668 if (fRC->isAA()) {
1669 aaBlitter.init(blitter, &fRC->aaRgn());
1670 blitter = &aaBlitter;
1671 }
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001672 }
1673
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 SkAutoKern autokern;
bungeman@google.com52c748b2011-08-22 21:30:43 +00001675 SkDraw1Glyph d1g;
reed@google.com045e62d2011-10-24 12:19:46 +00001676 SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677
1678 while (text < stop) {
bungeman@google.com2211b622012-01-13 15:02:58 +00001679 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680
1681 fx += autokern.adjust(glyph);
1682
1683 if (glyph.fWidth) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001684 proc(d1g, fx, fy, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001685 }
1686 fx += glyph.fAdvanceX;
1687 fy += glyph.fAdvanceY;
1688 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689}
1690
1691// last parameter is interpreted as SkFixed [x, y]
1692// return the fixed position, which may be rounded or not by the caller
1693// e.g. subpixel doesn't round
1694typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
1695
1696static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph,
1697 SkIPoint* dst) {
1698 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
1699}
1700
1701static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph,
1702 SkIPoint* dst) {
1703 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
1704 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
1705}
1706
1707static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph,
1708 SkIPoint* dst) {
1709 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
1710 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
1711}
1712
1713static AlignProc pick_align_proc(SkPaint::Align align) {
1714 static const AlignProc gProcs[] = {
1715 leftAlignProc, centerAlignProc, rightAlignProc
1716 };
reed@google.coma76de3d2011-01-13 18:30:42 +00001717
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
1719
1720 return gProcs[align];
1721}
1722
1723class TextMapState {
1724public:
1725 mutable SkPoint fLoc;
reed@google.coma76de3d2011-01-13 18:30:42 +00001726
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727 TextMapState(const SkMatrix& matrix, SkScalar y)
1728 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
1729
1730 typedef void (*Proc)(const TextMapState&, const SkScalar pos[]);
reed@google.coma76de3d2011-01-13 18:30:42 +00001731
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 Proc pickProc(int scalarsPerPosition);
reed@google.coma76de3d2011-01-13 18:30:42 +00001733
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734private:
1735 const SkMatrix& fMatrix;
1736 SkMatrix::MapXYProc fProc;
1737 SkScalar fY; // ignored by MapXYProc
1738 // these are only used by Only... procs
1739 SkScalar fScaleX, fTransX, fTransformedY;
1740
1741 static void MapXProc(const TextMapState& state, const SkScalar pos[]) {
1742 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
1743 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001744
reed@android.com8a1c16f2008-12-17 15:59:43 +00001745 static void MapXYProc(const TextMapState& state, const SkScalar pos[]) {
1746 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
1747 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001748
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749 static void MapOnlyScaleXProc(const TextMapState& state,
1750 const SkScalar pos[]) {
1751 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
1752 state.fTransformedY);
1753 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001754
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755 static void MapOnlyTransXProc(const TextMapState& state,
1756 const SkScalar pos[]) {
1757 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
1758 }
1759};
1760
1761TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) {
1762 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
reed@google.coma76de3d2011-01-13 18:30:42 +00001763
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 if (1 == scalarsPerPosition) {
1765 unsigned mtype = fMatrix.getType();
1766 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
1767 return MapXProc;
1768 } else {
1769 fScaleX = fMatrix.getScaleX();
1770 fTransX = fMatrix.getTranslateX();
1771 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
1772 fMatrix.getTranslateY();
1773 return (mtype & SkMatrix::kScale_Mask) ?
1774 MapOnlyScaleXProc : MapOnlyTransXProc;
1775 }
1776 } else {
1777 return MapXYProc;
1778 }
1779}
1780
1781//////////////////////////////////////////////////////////////////////////////
1782
1783void SkDraw::drawPosText(const char text[], size_t byteLength,
1784 const SkScalar pos[], SkScalar constY,
1785 int scalarsPerPosition, const SkPaint& paint) const {
1786 SkASSERT(byteLength == 0 || text != NULL);
1787 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1788
reed@android.comf2b98d62010-12-20 18:26:13 +00001789 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790
1791 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001792 if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793 return;
1794 }
1795
1796 if (/*paint.isLinearText() ||*/
tomhudson@google.com8d430182011-06-06 19:11:19 +00001797 (fMatrix->hasPerspective())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 // TODO !!!!
1799// this->drawText_asPaths(text, byteLength, x, y, paint);
1800 return;
1801 }
1802
reed@android.comf2b98d62010-12-20 18:26:13 +00001803 const SkMatrix* matrix = fMatrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001804
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
reed@android.comf2b98d62010-12-20 18:26:13 +00001806 SkAutoGlyphCache autoCache(paint, matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 SkGlyphCache* cache = autoCache.getCache();
reed@google.coma76de3d2011-01-13 18:30:42 +00001808
reed@google.com045e62d2011-10-24 12:19:46 +00001809 SkAAClipBlitterWrapper wrapper;
1810 SkAutoBlitterChoose blitterChooser;
1811 SkBlitter* blitter = NULL;
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001812 if (needsRasterTextBlit(*this)) {
reed@google.com045e62d2011-10-24 12:19:46 +00001813 blitterChooser.choose(*fBitmap, *matrix, paint);
1814 blitter = blitterChooser.get();
1815 if (fRC->isAA()) {
1816 wrapper.init(*fRC, blitter);
1817 blitter = wrapper.getBlitter();
1818 }
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001819 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001820
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821 const char* stop = text + byteLength;
1822 AlignProc alignProc = pick_align_proc(paint.getTextAlign());
bungeman@google.com2211b622012-01-13 15:02:58 +00001823 SkDraw1Glyph d1g;
1824 SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache);
reed@android.comf2b98d62010-12-20 18:26:13 +00001825 TextMapState tms(*matrix, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826 TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
1827
bungeman@google.com2211b622012-01-13 15:02:58 +00001828 if (cache->isSubpixel()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 // maybe we should skip the rounding if linearText is set
reed@google.comcb6ccdd2011-08-23 21:30:47 +00001830 SkAxisAlignment roundBaseline = SkComputeAxisAlignmentForHText(*matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831
1832 if (SkPaint::kLeft_Align == paint.getTextAlign()) {
1833 while (text < stop) {
reed@google.coma76de3d2011-01-13 18:30:42 +00001834
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835 tmsProc(tms, pos);
reed@google.coma76de3d2011-01-13 18:30:42 +00001836
bungeman@google.com2211b622012-01-13 15:02:58 +00001837#ifdef SK_DRAW_POS_TEXT_IGNORE_SUBPIXEL_LEFT_ALIGN_FIX
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 SkFixed fx = SkScalarToFixed(tms.fLoc.fX);
1839 SkFixed fy = SkScalarToFixed(tms.fLoc.fY);
bungeman@google.com2211b622012-01-13 15:02:58 +00001840#else
1841 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + (SK_FixedHalf >> SkGlyph::kSubBits);
1842 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + (SK_FixedHalf >> SkGlyph::kSubBits);
1843#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001844 SkFixed fxMask = ~0;
1845 SkFixed fyMask = ~0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001846
reed@google.comcb6ccdd2011-08-23 21:30:47 +00001847 if (kX_SkAxisAlignment == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001848 fyMask = 0;
reed@google.comcb6ccdd2011-08-23 21:30:47 +00001849 } else if (kY_SkAxisAlignment == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001850 fxMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001852
reed@android.comf2b98d62010-12-20 18:26:13 +00001853 const SkGlyph& glyph = glyphCacheProc(cache, &text,
1854 fx & fxMask, fy & fyMask);
reed@google.coma76de3d2011-01-13 18:30:42 +00001855
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856 if (glyph.fWidth) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001857 proc(d1g, fx, fy, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858 }
1859 pos += scalarsPerPosition;
1860 }
1861 } else {
1862 while (text < stop) {
bungeman@google.com9330cfe2012-01-04 14:17:00 +00001863 const char* currentText = text;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864 const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001865
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866 if (glyph->fWidth) {
1867 SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;)
1868 SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;)
1869
1870 SkFixed fx, fy;
reed@android.comf2b98d62010-12-20 18:26:13 +00001871 SkFixed fxMask = ~0;
1872 SkFixed fyMask = ~0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001873 tmsProc(tms, pos);
reed@google.coma76de3d2011-01-13 18:30:42 +00001874
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875 {
1876 SkIPoint fixedLoc;
1877 alignProc(tms.fLoc, *glyph, &fixedLoc);
bungeman@google.com2211b622012-01-13 15:02:58 +00001878 fx = fixedLoc.fX + (SK_FixedHalf >> SkGlyph::kSubBits);
1879 fy = fixedLoc.fY + (SK_FixedHalf >> SkGlyph::kSubBits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880
reed@google.comcb6ccdd2011-08-23 21:30:47 +00001881 if (kX_SkAxisAlignment == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001882 fyMask = 0;
reed@google.comcb6ccdd2011-08-23 21:30:47 +00001883 } else if (kY_SkAxisAlignment == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001884 fxMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885 }
1886 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001887
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888 // have to call again, now that we've been "aligned"
bungeman@google.com9330cfe2012-01-04 14:17:00 +00001889 glyph = &glyphCacheProc(cache, &currentText,
1890 fx & fxMask, fy & fyMask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891 // the assumption is that the advance hasn't changed
1892 SkASSERT(prevAdvX == glyph->fAdvanceX);
1893 SkASSERT(prevAdvY == glyph->fAdvanceY);
reed@google.coma76de3d2011-01-13 18:30:42 +00001894
reed@android.comf2b98d62010-12-20 18:26:13 +00001895 proc(d1g, fx, fy, *glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896 }
1897 pos += scalarsPerPosition;
1898 }
1899 }
1900 } else { // not subpixel
reed@google.comaeb07862012-04-18 18:32:04 +00001901 if (SkPaint::kLeft_Align == paint.getTextAlign()) {
1902 while (text < stop) {
1903 // the last 2 parameters are ignored
1904 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001905
reed@google.comaeb07862012-04-18 18:32:04 +00001906 if (glyph.fWidth) {
1907 tmsProc(tms, pos);
reed@google.coma76de3d2011-01-13 18:30:42 +00001908
reed@google.comaeb07862012-04-18 18:32:04 +00001909 proc(d1g,
1910 SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf,
1911 SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf,
1912 glyph);
1913 }
1914 pos += scalarsPerPosition;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 }
reed@google.comaeb07862012-04-18 18:32:04 +00001916 } else {
1917 while (text < stop) {
1918 // the last 2 parameters are ignored
1919 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1920
1921 if (glyph.fWidth) {
1922 tmsProc(tms, pos);
1923
1924 SkIPoint fixedLoc;
1925 alignProc(tms.fLoc, glyph, &fixedLoc);
1926
1927 proc(d1g,
1928 fixedLoc.fX + SK_FixedHalf,
1929 fixedLoc.fY + SK_FixedHalf,
1930 glyph);
1931 }
1932 pos += scalarsPerPosition;
1933 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934 }
1935 }
1936}
1937
1938#if defined _WIN32 && _MSC_VER >= 1300
1939#pragma warning ( pop )
1940#endif
1941
1942///////////////////////////////////////////////////////////////////////////////
1943
1944#include "SkPathMeasure.h"
1945
1946static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
1947 SkPathMeasure& meas, const SkMatrix& matrix) {
1948 SkMatrix::MapXYProc proc = matrix.getMapXYProc();
1949
1950 for (int i = 0; i < count; i++) {
1951 SkPoint pos;
1952 SkVector tangent;
1953
1954 proc(matrix, src[i].fX, src[i].fY, &pos);
1955 SkScalar sx = pos.fX;
1956 SkScalar sy = pos.fY;
1957
reed@google.com8f17b0d2012-04-12 13:24:30 +00001958 if (!meas.getPosTan(sx, &pos, &tangent)) {
1959 // set to 0 if the measure failed, so that we just set dst == pos
1960 tangent.set(0, 0);
1961 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962
1963 /* This is the old way (that explains our approach but is way too slow
1964 SkMatrix matrix;
1965 SkPoint pt;
1966
1967 pt.set(sx, sy);
1968 matrix.setSinCos(tangent.fY, tangent.fX);
1969 matrix.preTranslate(-sx, 0);
1970 matrix.postTranslate(pos.fX, pos.fY);
1971 matrix.mapPoints(&dst[i], &pt, 1);
1972 */
1973 dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
1974 pos.fY + SkScalarMul(tangent.fX, sy));
1975 }
1976}
1977
1978/* TODO
1979
1980 Need differentially more subdivisions when the follow-path is curvy. Not sure how to
1981 determine that, but we need it. I guess a cheap answer is let the caller tell us,
1982 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
1983*/
1984static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
1985 const SkMatrix& matrix) {
1986 SkPath::Iter iter(src, false);
1987 SkPoint srcP[4], dstP[3];
1988 SkPath::Verb verb;
1989
1990 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
1991 switch (verb) {
1992 case SkPath::kMove_Verb:
1993 morphpoints(dstP, srcP, 1, meas, matrix);
1994 dst->moveTo(dstP[0]);
1995 break;
1996 case SkPath::kLine_Verb:
1997 // turn lines into quads to look bendy
1998 srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
1999 srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
2000 morphpoints(dstP, srcP, 2, meas, matrix);
2001 dst->quadTo(dstP[0], dstP[1]);
2002 break;
2003 case SkPath::kQuad_Verb:
2004 morphpoints(dstP, &srcP[1], 2, meas, matrix);
2005 dst->quadTo(dstP[0], dstP[1]);
2006 break;
2007 case SkPath::kCubic_Verb:
2008 morphpoints(dstP, &srcP[1], 3, meas, matrix);
2009 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
2010 break;
2011 case SkPath::kClose_Verb:
2012 dst->close();
2013 break;
2014 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002015 SkDEBUGFAIL("unknown verb");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 break;
2017 }
2018 }
2019}
2020
2021void SkDraw::drawTextOnPath(const char text[], size_t byteLength,
2022 const SkPath& follow, const SkMatrix* matrix,
2023 const SkPaint& paint) const {
2024 SkASSERT(byteLength == 0 || text != NULL);
2025
2026 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00002027 if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 return;
2029 }
2030
djsollen@google.com166e6532012-03-20 14:24:38 +00002031 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 SkPathMeasure meas(follow, false);
2033 SkScalar hOffset = 0;
2034
2035 // need to measure first
2036 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
2037 SkScalar pathLen = meas.getLength();
2038 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2039 pathLen = SkScalarHalf(pathLen);
2040 }
2041 hOffset += pathLen;
2042 }
2043
2044 const SkPath* iterPath;
2045 SkScalar xpos;
2046 SkMatrix scaledMatrix;
2047 SkScalar scale = iter.getPathScale();
2048
2049 scaledMatrix.setScale(scale, scale);
reed@google.coma76de3d2011-01-13 18:30:42 +00002050
reed@google.com7b4531f2012-08-07 15:53:00 +00002051 while (iter.next(&iterPath, &xpos)) {
2052 if (iterPath) {
2053 SkPath tmp;
2054 SkMatrix m(scaledMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055
reed@google.com7b4531f2012-08-07 15:53:00 +00002056 m.postTranslate(xpos + hOffset, 0);
2057 if (matrix) {
2058 m.postConcat(*matrix);
2059 }
2060 morphpath(&tmp, *iterPath, meas, m);
2061 if (fDevice) {
2062 fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true);
2063 } else {
2064 this->drawPath(tmp, iter.getPaint(), NULL, true);
2065 }
reed@android.comf2b98d62010-12-20 18:26:13 +00002066 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067 }
2068}
2069
djsollen@google.com56c69772011-11-08 19:00:26 +00002070#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002071void SkDraw::drawPosTextOnPath(const char text[], size_t byteLength,
2072 const SkPoint pos[], const SkPaint& paint,
2073 const SkPath& path, const SkMatrix* matrix) const {
2074 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00002075 if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002076 return;
2077 }
2078
2079 SkMatrix scaledMatrix;
2080 SkPathMeasure meas(path, false);
2081
2082 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(
2083 SkPaint::kForward_TextBufferDirection, true);
2084
2085 // Copied (modified) from SkTextToPathIter constructor to setup paint
2086 SkPaint tempPaint(paint);
2087
2088 tempPaint.setLinearText(true);
2089 tempPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2090
2091 if (tempPaint.getPathEffect() == NULL && !(tempPaint.getStrokeWidth() > 0
2092 && tempPaint.getStyle() != SkPaint::kFill_Style)) {
2093 tempPaint.setStyle(SkPaint::kFill_Style);
2094 tempPaint.setPathEffect(NULL);
2095 }
2096 // End copied from SkTextToPathIter constructor
2097
2098 // detach cache
2099 SkGlyphCache* cache = tempPaint.detachCache(NULL);
2100
2101 // Must set scale, even if 1
2102 SkScalar scale = SK_Scalar1;
2103 scaledMatrix.setScale(scale, scale);
2104
2105 // Loop over all glyph ids
2106 for (const char* stop = text + byteLength; text < stop; pos++) {
2107
2108 const SkGlyph& glyph = glyphCacheProc(cache, &text);
2109 SkPath tmp;
2110
2111 const SkPath* glyphPath = cache->findPath(glyph);
2112 if (glyphPath == NULL) {
2113 continue;
2114 }
2115
2116 SkMatrix m(scaledMatrix);
2117 m.postTranslate(pos->fX, 0);
2118
2119 if (matrix) {
2120 m.postConcat(*matrix);
2121 }
2122
2123 morphpath(&tmp, *glyphPath, meas, m);
2124 this->drawPath(tmp, tempPaint);
2125
2126 }
2127
2128 // re-attach cache
2129 SkGlyphCache::AttachCache(cache);
2130}
2131#endif
2132
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133///////////////////////////////////////////////////////////////////////////////
2134
2135struct VertState {
2136 int f0, f1, f2;
2137
2138 VertState(int vCount, const uint16_t indices[], int indexCount)
2139 : fIndices(indices) {
2140 fCurrIndex = 0;
2141 if (indices) {
2142 fCount = indexCount;
2143 } else {
2144 fCount = vCount;
2145 }
2146 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002147
2148 typedef bool (*Proc)(VertState*);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 Proc chooseProc(SkCanvas::VertexMode mode);
2150
2151private:
2152 int fCount;
2153 int fCurrIndex;
2154 const uint16_t* fIndices;
reed@google.coma76de3d2011-01-13 18:30:42 +00002155
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156 static bool Triangles(VertState*);
2157 static bool TrianglesX(VertState*);
2158 static bool TriangleStrip(VertState*);
2159 static bool TriangleStripX(VertState*);
2160 static bool TriangleFan(VertState*);
2161 static bool TriangleFanX(VertState*);
2162};
2163
2164bool VertState::Triangles(VertState* state) {
2165 int index = state->fCurrIndex;
2166 if (index + 3 > state->fCount) {
2167 return false;
2168 }
2169 state->f0 = index + 0;
2170 state->f1 = index + 1;
2171 state->f2 = index + 2;
2172 state->fCurrIndex = index + 3;
2173 return true;
2174}
2175
2176bool VertState::TrianglesX(VertState* state) {
2177 const uint16_t* indices = state->fIndices;
2178 int index = state->fCurrIndex;
2179 if (index + 3 > state->fCount) {
2180 return false;
2181 }
2182 state->f0 = indices[index + 0];
2183 state->f1 = indices[index + 1];
2184 state->f2 = indices[index + 2];
2185 state->fCurrIndex = index + 3;
2186 return true;
2187}
2188
2189bool VertState::TriangleStrip(VertState* state) {
2190 int index = state->fCurrIndex;
2191 if (index + 3 > state->fCount) {
2192 return false;
2193 }
2194 state->f2 = index + 2;
2195 if (index & 1) {
2196 state->f0 = index + 1;
2197 state->f1 = index + 0;
2198 } else {
2199 state->f0 = index + 0;
2200 state->f1 = index + 1;
2201 }
2202 state->fCurrIndex = index + 1;
2203 return true;
2204}
2205
2206bool VertState::TriangleStripX(VertState* state) {
2207 const uint16_t* indices = state->fIndices;
2208 int index = state->fCurrIndex;
2209 if (index + 3 > state->fCount) {
2210 return false;
2211 }
2212 state->f2 = indices[index + 2];
2213 if (index & 1) {
2214 state->f0 = indices[index + 1];
2215 state->f1 = indices[index + 0];
2216 } else {
2217 state->f0 = indices[index + 0];
2218 state->f1 = indices[index + 1];
2219 }
2220 state->fCurrIndex = index + 1;
2221 return true;
2222}
2223
2224bool VertState::TriangleFan(VertState* state) {
2225 int index = state->fCurrIndex;
2226 if (index + 3 > state->fCount) {
2227 return false;
2228 }
2229 state->f0 = 0;
2230 state->f1 = index + 1;
2231 state->f2 = index + 2;
2232 state->fCurrIndex = index + 1;
2233 return true;
2234}
2235
2236bool VertState::TriangleFanX(VertState* state) {
2237 const uint16_t* indices = state->fIndices;
2238 int index = state->fCurrIndex;
2239 if (index + 3 > state->fCount) {
2240 return false;
2241 }
2242 state->f0 = indices[0];
2243 state->f1 = indices[index + 1];
2244 state->f2 = indices[index + 2];
2245 state->fCurrIndex = index + 1;
2246 return true;
2247}
2248
2249VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) {
2250 switch (mode) {
2251 case SkCanvas::kTriangles_VertexMode:
2252 return fIndices ? TrianglesX : Triangles;
2253 case SkCanvas::kTriangleStrip_VertexMode:
2254 return fIndices ? TriangleStripX : TriangleStrip;
2255 case SkCanvas::kTriangleFan_VertexMode:
2256 return fIndices ? TriangleFanX : TriangleFan;
2257 default:
2258 return NULL;
2259 }
2260}
2261
reed@google.com045e62d2011-10-24 12:19:46 +00002262typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRasterClip&,
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263 SkBlitter*);
2264
2265static HairProc ChooseHairProc(bool doAntiAlias) {
2266 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
2267}
2268
2269static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
2270 const SkPoint texs[], SkMatrix* matrix) {
2271 SkPoint src[3], dst[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00002272
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 src[0] = texs[state.f0];
2274 src[1] = texs[state.f1];
2275 src[2] = texs[state.f2];
2276 dst[0] = verts[state.f0];
2277 dst[1] = verts[state.f1];
2278 dst[2] = verts[state.f2];
2279 return matrix->setPolyToPoly(src, dst, 3);
2280}
2281
2282class SkTriColorShader : public SkShader {
2283public:
2284 SkTriColorShader() {}
2285
2286 bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
reed@google.coma76de3d2011-01-13 18:30:42 +00002287
reed@google.coma641f3f2012-12-13 22:16:30 +00002288 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
reed@google.coma76de3d2011-01-13 18:30:42 +00002289
djsollen@google.comba28d032012-03-26 17:57:35 +00002290 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTriColorShader)
2291
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292protected:
2293 SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {}
reed@google.coma76de3d2011-01-13 18:30:42 +00002294
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295private:
2296 SkMatrix fDstToUnit;
2297 SkPMColor fColors[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00002298
reed@android.com8a1c16f2008-12-17 15:59:43 +00002299 typedef SkShader INHERITED;
2300};
2301
2302bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[],
2303 int index0, int index1, int index2) {
reed@google.coma76de3d2011-01-13 18:30:42 +00002304
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 fColors[0] = SkPreMultiplyColor(colors[index0]);
2306 fColors[1] = SkPreMultiplyColor(colors[index1]);
2307 fColors[2] = SkPreMultiplyColor(colors[index2]);
reed@google.coma76de3d2011-01-13 18:30:42 +00002308
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 SkMatrix m, im;
2310 m.reset();
2311 m.set(0, pts[index1].fX - pts[index0].fX);
2312 m.set(1, pts[index2].fX - pts[index0].fX);
2313 m.set(2, pts[index0].fX);
2314 m.set(3, pts[index1].fY - pts[index0].fY);
2315 m.set(4, pts[index2].fY - pts[index0].fY);
2316 m.set(5, pts[index0].fY);
2317 if (!m.invert(&im)) {
2318 return false;
2319 }
2320 return fDstToUnit.setConcat(im, this->getTotalInverse());
2321}
2322
2323#include "SkColorPriv.h"
reed@android.com689411a2008-12-18 02:52:32 +00002324#include "SkComposeShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325
2326static int ScalarTo256(SkScalar v) {
2327 int scale = SkScalarToFixed(v) >> 8;
2328 if (scale < 0) {
2329 scale = 0;
2330 }
2331 if (scale > 255) {
2332 scale = 255;
2333 }
2334 return SkAlpha255To256(scale);
2335}
2336
2337void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
2338 SkPoint src;
reed@google.coma76de3d2011-01-13 18:30:42 +00002339
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 for (int i = 0; i < count; i++) {
2341 fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
2342 x += 1;
reed@google.coma76de3d2011-01-13 18:30:42 +00002343
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 int scale1 = ScalarTo256(src.fX);
2345 int scale2 = ScalarTo256(src.fY);
2346 int scale0 = 256 - scale1 - scale2;
2347 if (scale0 < 0) {
2348 if (scale1 > scale2) {
2349 scale2 = 256 - scale1;
2350 } else {
2351 scale1 = 256 - scale2;
2352 }
2353 scale0 = 0;
2354 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002355
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
2357 SkAlphaMulQ(fColors[1], scale1) +
2358 SkAlphaMulQ(fColors[2], scale2);
2359 }
2360}
2361
2362void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
2363 const SkPoint vertices[], const SkPoint textures[],
2364 const SkColor colors[], SkXfermode* xmode,
2365 const uint16_t indices[], int indexCount,
2366 const SkPaint& paint) const {
2367 SkASSERT(0 == count || NULL != vertices);
reed@google.coma76de3d2011-01-13 18:30:42 +00002368
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 // abort early if there is nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00002370 if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 return;
2372 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002373
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 // transform out vertices into device coordinates
2375 SkAutoSTMalloc<16, SkPoint> storage(count);
2376 SkPoint* devVerts = storage.get();
2377 fMatrix->mapPoints(devVerts, vertices, count);
reed@google.coma76de3d2011-01-13 18:30:42 +00002378
reed@android.com8a1c16f2008-12-17 15:59:43 +00002379 if (fBounder) {
2380 SkRect bounds;
2381 bounds.set(devVerts, count);
2382 if (!fBounder->doRect(bounds, paint)) {
2383 return;
2384 }
2385 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002386
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387 /*
2388 We can draw the vertices in 1 of 4 ways:
2389
2390 - solid color (no shader/texture[], no colors[])
2391 - just colors (no shader/texture[], has colors[])
2392 - just texture (has shader/texture[], no colors[])
2393 - colors * texture (has shader/texture[], has colors[])
reed@google.coma76de3d2011-01-13 18:30:42 +00002394
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395 Thus for texture drawing, we need both texture[] and a shader.
2396 */
2397
2398 SkTriColorShader triShader; // must be above declaration of p
2399 SkPaint p(paint);
2400
2401 SkShader* shader = p.getShader();
2402 if (NULL == shader) {
2403 // if we have no shader, we ignore the texture coordinates
2404 textures = NULL;
2405 } else if (NULL == textures) {
2406 // if we don't have texture coordinates, ignore the shader
2407 p.setShader(NULL);
2408 shader = NULL;
2409 }
2410
2411 // setup the custom shader (if needed)
2412 if (NULL != colors) {
2413 if (NULL == textures) {
2414 // just colors (no texture)
reed@google.coma641f3f2012-12-13 22:16:30 +00002415 shader = p.setShader(&triShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 } else {
2417 // colors * texture
2418 SkASSERT(shader);
2419 bool releaseMode = false;
2420 if (NULL == xmode) {
reed@android.com845fdac2009-06-23 03:01:32 +00002421 xmode = SkXfermode::Create(SkXfermode::kMultiply_Mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002422 releaseMode = true;
2423 }
2424 SkShader* compose = SkNEW_ARGS(SkComposeShader,
2425 (&triShader, shader, xmode));
2426 p.setShader(compose)->unref();
2427 if (releaseMode) {
2428 xmode->unref();
2429 }
reed@google.coma641f3f2012-12-13 22:16:30 +00002430 shader = compose;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 }
2432 }
2433
2434 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p);
reed@google.comea033602012-12-14 13:13:55 +00002435 // important that we abort early, as below we may manipulate the shader
2436 // and that is only valid if the shader returned true from setContext.
2437 // If it returned false, then our blitter will be the NullBlitter.
2438 if (blitter->isNullBlitter()) {
2439 return;
2440 }
2441
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442 // setup our state and function pointer for iterating triangles
2443 VertState state(count, indices, indexCount);
2444 VertState::Proc vertProc = state.chooseProc(vmode);
reed@google.coma76de3d2011-01-13 18:30:42 +00002445
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446 if (NULL != textures || NULL != colors) {
bsalomon@google.comf94b3a42012-10-31 18:09:01 +00002447 SkMatrix tempM;
2448 SkMatrix savedLocalM;
2449 if (shader) {
2450 savedLocalM = shader->getLocalMatrix();
2451 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002452
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453 while (vertProc(&state)) {
2454 if (NULL != textures) {
2455 if (texture_to_matrix(state, vertices, textures, &tempM)) {
bsalomon@google.comf94b3a42012-10-31 18:09:01 +00002456 tempM.postConcat(savedLocalM);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457 shader->setLocalMatrix(tempM);
2458 // need to recal setContext since we changed the local matrix
reed@google.coma641f3f2012-12-13 22:16:30 +00002459 shader->endContext();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460 if (!shader->setContext(*fBitmap, p, *fMatrix)) {
2461 continue;
2462 }
2463 }
2464 }
2465 if (NULL != colors) {
2466 if (!triShader.setup(vertices, colors,
2467 state.f0, state.f1, state.f2)) {
2468 continue;
2469 }
2470 }
reed@google.com045e62d2011-10-24 12:19:46 +00002471
2472 SkPoint tmp[] = {
2473 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
2474 };
2475 SkScan::FillTriangle(tmp, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002476 }
2477 // now restore the shader's original local matrix
2478 if (NULL != shader) {
bsalomon@google.comf94b3a42012-10-31 18:09:01 +00002479 shader->setLocalMatrix(savedLocalM);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002480 }
2481 } else {
2482 // no colors[] and no texture
2483 HairProc hairProc = ChooseHairProc(paint.isAntiAlias());
reed@google.com045e62d2011-10-24 12:19:46 +00002484 const SkRasterClip& clip = *fRC;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002485 while (vertProc(&state)) {
reed@google.com045e62d2011-10-24 12:19:46 +00002486 hairProc(devVerts[state.f0], devVerts[state.f1], clip, blitter.get());
2487 hairProc(devVerts[state.f1], devVerts[state.f2], clip, blitter.get());
2488 hairProc(devVerts[state.f2], devVerts[state.f0], clip, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002489 }
2490 }
2491}
2492
reed@google.com0a0a2362011-03-23 13:51:55 +00002493///////////////////////////////////////////////////////////////////////////////
2494///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002495
2496#ifdef SK_DEBUG
2497
reed@android.comf2b98d62010-12-20 18:26:13 +00002498void SkDraw::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002499 SkASSERT(fBitmap != NULL);
2500 SkASSERT(fMatrix != NULL);
2501 SkASSERT(fClip != NULL);
reed@google.com045e62d2011-10-24 12:19:46 +00002502 SkASSERT(fRC != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002503
reed@google.com045e62d2011-10-24 12:19:46 +00002504 const SkIRect& cr = fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 SkIRect br;
2506
reed@android.comf2b98d62010-12-20 18:26:13 +00002507 br.set(0, 0, fBitmap->width(), fBitmap->height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002508 SkASSERT(cr.isEmpty() || br.contains(cr));
2509}
2510
2511#endif
2512
reed@google.com0a0a2362011-03-23 13:51:55 +00002513///////////////////////////////////////////////////////////////////////////////
2514
2515SkBounder::SkBounder() {
2516 // initialize up front. This gets reset by SkCanvas before each draw call.
2517 fClip = &SkRegion::GetEmptyRegion();
2518}
reed@android.com8a1c16f2008-12-17 15:59:43 +00002519
2520bool SkBounder::doIRect(const SkIRect& r) {
2521 SkIRect rr;
2522 return rr.intersect(fClip->getBounds(), r) && this->onIRect(rr);
2523}
2524
reed@android.comd055c1f2010-03-01 14:54:05 +00002525// TODO: change the prototype to take fixed, and update the callers
2526bool SkBounder::doIRectGlyph(const SkIRect& r, int x, int y,
2527 const SkGlyph& glyph) {
reed@android.com474a12c2010-01-04 19:35:33 +00002528 SkIRect rr;
reed@android.comd055c1f2010-03-01 14:54:05 +00002529 if (!rr.intersect(fClip->getBounds(), r)) {
2530 return false;
2531 }
2532 GlyphRec rec;
2533 rec.fLSB.set(SkIntToFixed(x), SkIntToFixed(y));
2534 rec.fRSB.set(rec.fLSB.fX + glyph.fAdvanceX,
2535 rec.fLSB.fY + glyph.fAdvanceY);
2536 rec.fGlyphID = glyph.getGlyphID();
2537 rec.fFlags = 0;
2538 return this->onIRectGlyph(rr, rec);
reed@android.com474a12c2010-01-04 19:35:33 +00002539}
2540
reed@android.com8a1c16f2008-12-17 15:59:43 +00002541bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1,
2542 const SkPaint& paint) {
2543 SkIRect r;
2544 SkScalar v0, v1;
2545
2546 v0 = pt0.fX;
2547 v1 = pt1.fX;
2548 if (v0 > v1) {
2549 SkTSwap<SkScalar>(v0, v1);
2550 }
2551 r.fLeft = SkScalarFloor(v0);
2552 r.fRight = SkScalarCeil(v1);
2553
2554 v0 = pt0.fY;
2555 v1 = pt1.fY;
2556 if (v0 > v1) {
2557 SkTSwap<SkScalar>(v0, v1);
2558 }
2559 r.fTop = SkScalarFloor(v0);
2560 r.fBottom = SkScalarCeil(v1);
2561
2562 if (paint.isAntiAlias()) {
2563 r.inset(-1, -1);
2564 }
2565 return this->doIRect(r);
2566}
2567
2568bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint) {
2569 SkIRect r;
2570
2571 if (paint.getStyle() == SkPaint::kFill_Style) {
2572 rect.round(&r);
2573 } else {
2574 int rad = -1;
2575 rect.roundOut(&r);
2576 if (paint.isAntiAlias()) {
2577 rad = -2;
2578 }
2579 r.inset(rad, rad);
2580 }
2581 return this->doIRect(r);
2582}
2583
2584bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) {
reed@android.comd252db02009-04-01 18:31:44 +00002585 SkIRect r;
2586 const SkRect& bounds = path.getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002587
2588 if (doFill) {
2589 bounds.round(&r);
2590 } else { // hairline
2591 bounds.roundOut(&r);
2592 }
2593
2594 if (paint.isAntiAlias()) {
2595 r.inset(-1, -1);
2596 }
2597 return this->doIRect(r);
2598}
2599
2600void SkBounder::commit() {
2601 // override in subclass
2602}
2603
2604////////////////////////////////////////////////////////////////////////////////////////////////
2605
2606#include "SkPath.h"
2607#include "SkDraw.h"
2608#include "SkRegion.h"
2609#include "SkBlitter.h"
2610
2611static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
2612 SkMaskFilter* filter, const SkMatrix* filterMatrix,
2613 SkIRect* bounds) {
2614 if (devPath.isEmpty()) {
2615 return false;
2616 }
2617
reed@android.com8a1c16f2008-12-17 15:59:43 +00002618 // init our bounds from the path
2619 {
reed@android.comd252db02009-04-01 18:31:44 +00002620 SkRect pathBounds = devPath.getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002621 pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf);
2622 pathBounds.roundOut(bounds);
2623 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002624
tomhudson@google.com6db75fc2012-03-23 14:46:48 +00002625 SkIPoint margin = SkIPoint::Make(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002626 if (filter) {
2627 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00002628
bungeman@google.com5af16f82011-09-02 15:06:44 +00002629 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00002630
reed@android.com8a1c16f2008-12-17 15:59:43 +00002631 srcM.fBounds = *bounds;
2632 srcM.fFormat = SkMask::kA8_Format;
2633 srcM.fImage = NULL;
2634 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
2635 return false;
2636 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002637 }
2638
bungeman@google.com5af16f82011-09-02 15:06:44 +00002639 // (possibly) trim the bounds to reflect the clip
reed@android.com8a1c16f2008-12-17 15:59:43 +00002640 // (plus whatever slop the filter needs)
bungeman@google.com5af16f82011-09-02 15:06:44 +00002641 if (clipBounds) {
2642 SkIRect tmp = *clipBounds;
reed@android.com35555912009-03-16 18:46:55 +00002643 // Ugh. Guard against gigantic margins from wacky filters. Without this
2644 // check we can request arbitrary amounts of slop beyond our visible
2645 // clip, and bring down the renderer (at least on finite RAM machines
2646 // like handsets, etc.). Need to balance this invented value between
2647 // quality of large filters like blurs, and the corresponding memory
2648 // requests.
2649 static const int MAX_MARGIN = 128;
2650 tmp.inset(-SkMin32(margin.fX, MAX_MARGIN),
2651 -SkMin32(margin.fY, MAX_MARGIN));
bungeman@google.com5af16f82011-09-02 15:06:44 +00002652 if (!bounds->intersect(tmp)) {
2653 return false;
2654 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002655 }
2656
2657 return true;
2658}
2659
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002660static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
2661 SkPaint::Style style) {
reed@google.com045e62d2011-10-24 12:19:46 +00002662 SkBitmap bm;
2663 SkDraw draw;
2664 SkRasterClip clip;
2665 SkMatrix matrix;
2666 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002667
2668 bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes);
2669 bm.setPixels(mask.fImage);
2670
reed@google.com045e62d2011-10-24 12:19:46 +00002671 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002672 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
2673 -SkIntToScalar(mask.fBounds.fTop));
2674
2675 draw.fBitmap = &bm;
reed@google.com045e62d2011-10-24 12:19:46 +00002676 draw.fRC = &clip;
2677 draw.fClip = &clip.bwRgn();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002678 draw.fMatrix = &matrix;
2679 draw.fBounder = NULL;
2680 paint.setAntiAlias(true);
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002681 paint.setStyle(style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002682 draw.drawPath(devPath, paint);
2683}
2684
2685bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
2686 SkMaskFilter* filter, const SkMatrix* filterMatrix,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002687 SkMask* mask, SkMask::CreateMode mode,
2688 SkPaint::Style style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002689 if (SkMask::kJustRenderImage_CreateMode != mode) {
2690 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
2691 return false;
2692 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002693
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
2695 mask->fFormat = SkMask::kA8_Format;
2696 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00002697 size_t size = mask->computeImageSize();
2698 if (0 == size) {
2699 // we're too big to allocate the mask, abort
2700 return false;
2701 }
2702 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002703 memset(mask->fImage, 0, mask->computeImageSize());
2704 }
2705
2706 if (SkMask::kJustComputeBounds_CreateMode != mode) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002707 draw_into_mask(*mask, devPath, style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002708 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002709
reed@android.com8a1c16f2008-12-17 15:59:43 +00002710 return true;
2711}