blob: 7eb0be6b6b229a04ece584a747e5bdfaeecebd79 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkDraw.h"
9#include "SkBlitter.h"
10#include "SkBounder.h"
11#include "SkCanvas.h"
12#include "SkColorPriv.h"
13#include "SkDevice.h"
reed@google.com1c028bd2013-08-28 15:23:19 +000014#include "SkDeviceLooper.h"
bungeman@google.com2211b622012-01-13 15:02:58 +000015#include "SkFixed.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkMaskFilter.h"
17#include "SkPaint.h"
18#include "SkPathEffect.h"
reed@google.com045e62d2011-10-24 12:19:46 +000019#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkRasterizer.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000021#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkScan.h"
23#include "SkShader.h"
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000024#include "SkSmallAllocator.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +000025#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkStroke.h"
reed@google.com32e5d972011-07-27 18:25:57 +000027#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkUtils.h"
29
30#include "SkAutoKern.h"
31#include "SkBitmapProcShader.h"
32#include "SkDrawProcs.h"
reed@google.comae573582013-01-03 15:22:40 +000033#include "SkMatrixUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000034
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define TRACE_BITMAP_DRAWS
37
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
reed@google.comfd4236e2011-07-25 21:16:22 +000039/** Helper for allocating small blitters on the stack.
40 */
reed@google.com40c2ba22011-07-25 19:41:22 +000041class SkAutoBlitterChoose : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000042public:
reed@google.com1d6ee0b2011-07-05 17:44:56 +000043 SkAutoBlitterChoose() {
44 fBlitter = NULL;
45 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000046 SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix,
reed@google.com126f7f52013-11-07 16:06:53 +000047 const SkPaint& paint, bool drawCoverage = false) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000048 fBlitter = SkBlitter::Choose(device, matrix, paint, &fAllocator,
49 drawCoverage);
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000050 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
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);
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000058 fBlitter = SkBlitter::Choose(device, matrix, paint, &fAllocator);
reed@google.com1d6ee0b2011-07-05 17:44:56 +000059 }
60
reed@android.com8a1c16f2008-12-17 15:59:43 +000061private:
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000062 // Owned by fAllocator, which will handle the delete.
63 SkBlitter* fBlitter;
64 SkTBlitterAllocator fAllocator;
reed@android.com8a1c16f2008-12-17 15:59:43 +000065};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000066#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose)
reed@android.com8a1c16f2008-12-17 15:59:43 +000067
reed@google.com40c2ba22011-07-25 19:41:22 +000068/**
69 * Since we are providing the storage for the shader (to avoid the perf cost
70 * of calling new) we insist that in our destructor we can account for all
71 * owners of the shader.
72 */
73class SkAutoBitmapShaderInstall : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000074public:
reed@google.com40c2ba22011-07-25 19:41:22 +000075 SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint)
76 : fPaint(paint) /* makes a copy of the paint */ {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000077 fPaint.setShader(CreateBitmapShader(src, SkShader::kClamp_TileMode,
78 SkShader::kClamp_TileMode,
79 &fAllocator));
reed@google.com40c2ba22011-07-25 19:41:22 +000080 // we deliberately left the shader with an owner-count of 2
81 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 }
reed@google.com82065d62011-02-07 15:30:46 +000083
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 ~SkAutoBitmapShaderInstall() {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000085 // since fAllocator will destroy shader, we insist that owners == 2
86 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000087
reed@google.com40c2ba22011-07-25 19:41:22 +000088 fPaint.setShader(NULL); // unref the shader by 1
reed@android.com8a1c16f2008-12-17 15:59:43 +000089
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 }
reed@google.com82065d62011-02-07 15:30:46 +000091
reed@google.com40c2ba22011-07-25 19:41:22 +000092 // return the new paint that has the shader applied
93 const SkPaint& paintWithShader() const { return fPaint; }
94
reed@android.com8a1c16f2008-12-17 15:59:43 +000095private:
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000096 // copy of caller's paint (which we then modify)
97 SkPaint fPaint;
98 // Stores the shader.
99 SkTBlitterAllocator fAllocator;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000101#define SkAutoBitmapShaderInstall(...) SK_REQUIRE_LOCAL_VAR(SkAutoBitmapShaderInstall)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103///////////////////////////////////////////////////////////////////////////////
104
reed@android.comf2b98d62010-12-20 18:26:13 +0000105SkDraw::SkDraw() {
106 sk_bzero(this, sizeof(*this));
107}
108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109SkDraw::SkDraw(const SkDraw& src) {
110 memcpy(this, &src, sizeof(*this));
111}
112
reed@google.com4bbdeac2013-01-24 21:03:11 +0000113bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
114 if (fRC->isEmpty()) {
115 return false;
116 }
117
118 SkMatrix inverse;
119 if (!fMatrix->invert(&inverse)) {
120 return false;
121 }
122
123 SkIRect devBounds = fRC->getBounds();
124 // outset to have slop for antialasing and hairlines
125 devBounds.outset(1, 1);
126 inverse.mapRect(localBounds, SkRect::Make(devBounds));
127 return true;
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
131
132typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
133
134static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +0000135 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136}
137
138static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
139
140static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000141 sk_memset32((uint32_t*)pixels, data, SkToInt(bytes >> 2));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142}
143
144static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000145 sk_memset16((uint16_t*)pixels, data, SkToInt(bytes >> 1));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146}
147
148static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
149 memset(pixels, data, bytes);
150}
151
152static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap,
153 const SkPaint& paint,
154 uint32_t* data) {
155 // todo: we can apply colorfilter up front if no shader, so we wouldn't
156 // need to abort this fastpath
157 if (paint.getShader() || paint.getColorFilter()) {
158 return NULL;
159 }
160
reed@android.com845fdac2009-06-23 03:01:32 +0000161 SkXfermode::Mode mode;
mike@reedtribe.orgbe2aa2a2011-11-17 02:32:04 +0000162 if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 return NULL;
164 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 // collaps modes based on color...
reed@android.com845fdac2009-06-23 03:01:32 +0000169 if (SkXfermode::kSrcOver_Mode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 unsigned alpha = SkColorGetA(color);
171 if (0 == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000172 mode = SkXfermode::kDst_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 } else if (0xFF == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000174 mode = SkXfermode::kSrc_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 }
176 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 switch (mode) {
reed@android.com845fdac2009-06-23 03:01:32 +0000179 case SkXfermode::kClear_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180// SkDebugf("--- D_Clear_BitmapXferProc\n");
181 return D_Clear_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000182 case SkXfermode::kDst_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183// SkDebugf("--- D_Dst_BitmapXferProc\n");
184 return D_Dst_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000185 case SkXfermode::kSrc_Mode: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000187 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 */
189 SkPMColor pmc = SkPreMultiplyColor(color);
reed@google.com900ecf22014-02-20 20:55:37 +0000190 switch (bitmap.colorType()) {
commit-bot@chromium.org149e9a12014-04-09 20:45:29 +0000191 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 if (data) {
193 *data = pmc;
194 }
195// SkDebugf("--- D32_Src_BitmapXferProc\n");
196 return D32_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000197 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 if (data) {
199 *data = SkPixel32ToPixel16(pmc);
200 }
201// SkDebugf("--- D16_Src_BitmapXferProc\n");
202 return D16_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000203 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 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;
reed@google.com900ecf22014-02-20 20:55:37 +0000223 switch (bitmap.colorType()) {
commit-bot@chromium.org149e9a12014-04-09 20:45:29 +0000224 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 shiftPerPixel = 2;
226 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000227 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 shiftPerPixel = 1;
229 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000230 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 shiftPerPixel = 0;
232 break;
233 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000234 SkDEBUGFAIL("Can't use xferproc on this config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 return;
236 }
237
238 uint8_t* pixels = (uint8_t*)bitmap.getPixels();
239 SkASSERT(pixels);
240 const size_t rowBytes = bitmap.rowBytes();
241 const int widthBytes = rect.width() << shiftPerPixel;
242
243 // skip down to the first scanline and X position
244 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
245 for (int scans = rect.height() - 1; scans >= 0; --scans) {
246 proc(pixels, widthBytes, procData);
247 pixels += rowBytes;
248 }
249}
250
251void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000252 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
reed@google.com045e62d2011-10-24 12:19:46 +0000254 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 return;
256 }
257
258 SkIRect devRect;
259 devRect.set(0, 0, fBitmap->width(), fBitmap->height());
260 if (fBounder && !fBounder->doIRect(devRect)) {
261 return;
262 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000263
reed@google.com045e62d2011-10-24 12:19:46 +0000264 if (fRC->isBW()) {
265 /* If we don't have a shader (i.e. we're just a solid color) we may
266 be faster to operate directly on the device bitmap, rather than invoking
267 a blitter. Esp. true for xfermodes, which require a colorshader to be
268 present, which is just redundant work. Since we're drawing everywhere
269 in the clip, we don't have to worry about antialiasing.
270 */
271 uint32_t procData = 0; // to avoid the warning
272 BitmapXferProc proc = ChooseBitmapXferProc(*fBitmap, paint, &procData);
273 if (proc) {
274 if (D_Dst_BitmapXferProc == proc) { // nothing to do
275 return;
276 }
277
278 SkRegion::Iterator iter(fRC->bwRgn());
279 while (!iter.done()) {
280 CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData);
281 iter.next();
282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 return;
284 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com045e62d2011-10-24 12:19:46 +0000286
287 // normal case: use a blitter
288 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
289 SkScan::FillIRect(devRect, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290}
291
292///////////////////////////////////////////////////////////////////////////////
293
294struct PtProcRec {
295 SkCanvas::PointMode fMode;
296 const SkPaint* fPaint;
297 const SkRegion* fClip;
reed@google.com045e62d2011-10-24 12:19:46 +0000298 const SkRasterClip* fRC;
reed@google.coma76de3d2011-01-13 18:30:42 +0000299
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 // computed values
301 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
304 SkBlitter*);
305
306 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000307 const SkRasterClip*);
308 Proc chooseProc(SkBlitter** blitter);
309
310private:
311 SkAAClipBlitterWrapper fWrapper;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312};
313
314static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
315 int count, SkBlitter* blitter) {
316 SkASSERT(rec.fClip->isRect());
317 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000318
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000320 int x = SkScalarFloorToInt(devPts[i].fX);
321 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 if (r.contains(x, y)) {
323 blitter->blitH(x, y, 1);
324 }
325 }
326}
327
328static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
329 const SkPoint devPts[], int count,
330 SkBlitter* blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +0000331 SkASSERT(rec.fRC->isRect());
332 const SkIRect& r = rec.fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 uint32_t value;
334 const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
335 SkASSERT(bitmap);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000336
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 uint16_t* addr = bitmap->getAddr16(0, 0);
scroggo@google.come5f48242013-02-25 21:47:41 +0000338 size_t rb = bitmap->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000341 int x = SkScalarFloorToInt(devPts[i].fX);
342 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 if (r.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
345 }
346 }
347}
348
reed@google.com2d47a212012-11-29 21:01:00 +0000349static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
350 const SkPoint devPts[], int count,
351 SkBlitter* blitter) {
352 SkASSERT(rec.fRC->isRect());
353 const SkIRect& r = rec.fRC->getBounds();
354 uint32_t value;
355 const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
356 SkASSERT(bitmap);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000357
reed@google.com2d47a212012-11-29 21:01:00 +0000358 SkPMColor* addr = bitmap->getAddr32(0, 0);
scroggo@google.come5f48242013-02-25 21:47:41 +0000359 size_t rb = bitmap->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000360
reed@google.com2d47a212012-11-29 21:01:00 +0000361 for (int i = 0; i < count; i++) {
362 int x = SkScalarFloorToInt(devPts[i].fX);
363 int y = SkScalarFloorToInt(devPts[i].fY);
364 if (r.contains(x, y)) {
365 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
366 }
367 }
368}
369
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
371 int count, SkBlitter* blitter) {
372 for (int i = 0; i < count; i++) {
reed@google.come1ca7052013-12-17 19:22:07 +0000373 int x = SkScalarFloorToInt(devPts[i].fX);
374 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 if (rec.fClip->contains(x, y)) {
376 blitter->blitH(x, y, 1);
377 }
378 }
379}
380
381static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
382 int count, SkBlitter* blitter) {
383 for (int i = 0; i < count; i += 2) {
reed@google.com045e62d2011-10-24 12:19:46 +0000384 SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 }
386}
387
388static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
389 int count, SkBlitter* blitter) {
390 for (int i = 0; i < count - 1; i++) {
reed@google.com045e62d2011-10-24 12:19:46 +0000391 SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 }
393}
394
395// aa versions
396
397static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
398 int count, SkBlitter* blitter) {
399 for (int i = 0; i < count; i += 2) {
reed@google.com045e62d2011-10-24 12:19:46 +0000400 SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 }
402}
403
404static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
405 int count, SkBlitter* blitter) {
406 for (int i = 0; i < count - 1; i++) {
reed@google.com045e62d2011-10-24 12:19:46 +0000407 SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 }
409}
410
411// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
412
413static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
414 int count, SkBlitter* blitter) {
415 const SkFixed radius = rec.fRadius;
416 for (int i = 0; i < count; i++) {
417 SkFixed x = SkScalarToFixed(devPts[i].fX);
418 SkFixed y = SkScalarToFixed(devPts[i].fY);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 SkXRect r;
421 r.fLeft = x - radius;
422 r.fTop = y - radius;
423 r.fRight = x + radius;
424 r.fBottom = y + radius;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000425
reed@google.com045e62d2011-10-24 12:19:46 +0000426 SkScan::FillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 }
428}
429
430static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
431 int count, SkBlitter* blitter) {
432 const SkFixed radius = rec.fRadius;
433 for (int i = 0; i < count; i++) {
434 SkFixed x = SkScalarToFixed(devPts[i].fX);
435 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 SkXRect r;
438 r.fLeft = x - radius;
439 r.fTop = y - radius;
440 r.fRight = x + radius;
441 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000442
reed@google.com045e62d2011-10-24 12:19:46 +0000443 SkScan::AntiFillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 }
445}
446
reed@android.comb4f404a2009-07-10 17:02:17 +0000447// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
reed@google.com045e62d2011-10-24 12:19:46 +0000449 const SkMatrix* matrix, const SkRasterClip* rc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 if (paint.getPathEffect()) {
451 return false;
452 }
453 SkScalar width = paint.getStrokeWidth();
454 if (0 == width) {
455 fMode = mode;
456 fPaint = &paint;
reed@google.com045e62d2011-10-24 12:19:46 +0000457 fClip = NULL;
458 fRC = rc;
reed@google.com2d47a212012-11-29 21:01:00 +0000459 fRadius = SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 return true;
461 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000462 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
463 matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
465 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
466 if (SkScalarNearlyZero(sx - sy)) {
467 if (sx < 0) {
468 sx = -sx;
469 }
470
471 fMode = mode;
472 fPaint = &paint;
reed@google.com045e62d2011-10-24 12:19:46 +0000473 fClip = NULL;
474 fRC = rc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
476 return true;
477 }
478 }
479 return false;
480}
481
reed@google.com045e62d2011-10-24 12:19:46 +0000482PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
reed@android.comb4f404a2009-07-10 17:02:17 +0000483 Proc proc = NULL;
reed@google.coma76de3d2011-01-13 18:30:42 +0000484
reed@google.com045e62d2011-10-24 12:19:46 +0000485 SkBlitter* blitter = *blitterPtr;
486 if (fRC->isBW()) {
487 fClip = &fRC->bwRgn();
488 } else {
489 fWrapper.init(*fRC, blitter);
490 fClip = &fWrapper.getRgn();
491 blitter = fWrapper.getBlitter();
492 *blitterPtr = blitter;
493 }
494
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 // for our arrays
496 SkASSERT(0 == SkCanvas::kPoints_PointMode);
497 SkASSERT(1 == SkCanvas::kLines_PointMode);
498 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
499 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
500
reed@google.com2d47a212012-11-29 21:01:00 +0000501 if (fPaint->isAntiAlias()) {
502 if (0 == fPaint->getStrokeWidth()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 static const Proc gAAProcs[] = {
504 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
505 };
506 proc = gAAProcs[fMode];
reed@google.com2d47a212012-11-29 21:01:00 +0000507 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
508 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
509 proc = aa_square_proc;
510 }
511 } else { // BW
512 if (fRadius <= SK_FixedHalf) { // small radii and hairline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
514 uint32_t value;
515 const SkBitmap* bm = blitter->justAnOpaqueColor(&value);
reed@google.com900ecf22014-02-20 20:55:37 +0000516 if (bm && kRGB_565_SkColorType == bm->colorType()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 proc = bw_pt_rect_16_hair_proc;
commit-bot@chromium.org149e9a12014-04-09 20:45:29 +0000518 } else if (bm && kN32_SkColorType == bm->colorType()) {
reed@google.com2d47a212012-11-29 21:01:00 +0000519 proc = bw_pt_rect_32_hair_proc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 } else {
521 proc = bw_pt_rect_hair_proc;
522 }
523 } else {
524 static Proc gBWProcs[] = {
525 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
526 };
527 proc = gBWProcs[fMode];
528 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 } else {
530 proc = bw_square_proc;
531 }
532 }
533 return proc;
534}
535
536static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode,
537 size_t count, const SkPoint pts[],
538 const SkPaint& paint, const SkMatrix& matrix) {
539 SkIRect ibounds;
540 SkRect bounds;
541 SkScalar inset = paint.getStrokeWidth();
542
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000543 bounds.set(pts, SkToInt(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 bounds.inset(-inset, -inset);
545 matrix.mapRect(&bounds);
546
547 bounds.roundOut(&ibounds);
548 return bounder->doIRect(ibounds);
549}
550
551// each of these costs 8-bytes of stack space, so don't make it too large
552// must be even for lines/polygon to work
553#define MAX_DEV_PTS 32
554
555void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000556 const SkPoint pts[], const SkPaint& paint,
557 bool forceUseDevice) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 // if we're in lines mode, force count to be even
559 if (SkCanvas::kLines_PointMode == mode) {
560 count &= ~(size_t)1;
561 }
562
563 if ((long)count <= 0) {
564 return;
565 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000566
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 SkASSERT(pts != NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000568 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000569
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000571 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 return;
573 }
574
reed@google.comfd4236e2011-07-25 21:16:22 +0000575 if (fBounder) {
576 if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
577 return;
578 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000579
reed@google.comfd4236e2011-07-25 21:16:22 +0000580 // clear the bounder and call this again, so we don't invoke the bounder
581 // later if we happen to call ourselves for drawRect, drawPath, etc.
582 SkDraw noBounder(*this);
583 noBounder.fBounder = NULL;
584 noBounder.drawPoints(mode, count, pts, paint, forceUseDevice);
585 return;
586 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000587
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 PtProcRec rec;
reed@google.com045e62d2011-10-24 12:19:46 +0000589 if (!forceUseDevice && rec.init(mode, paint, fMatrix, fRC)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
591
592 SkPoint devPts[MAX_DEV_PTS];
593 const SkMatrix* matrix = fMatrix;
594 SkBlitter* bltr = blitter.get();
reed@google.com045e62d2011-10-24 12:19:46 +0000595 PtProcRec::Proc proc = rec.chooseProc(&bltr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 // we have to back up subsequent passes if we're in polygon mode
597 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 do {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000600 int n = SkToInt(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 if (n > MAX_DEV_PTS) {
602 n = MAX_DEV_PTS;
603 }
604 matrix->mapPoints(devPts, pts, n);
605 proc(rec, devPts, n, bltr);
606 pts += n - backup;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000607 SkASSERT(SkToInt(count) >= n);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 count -= n;
609 if (count > 0) {
610 count += backup;
611 }
612 } while (count != 0);
613 } else {
614 switch (mode) {
615 case SkCanvas::kPoints_PointMode: {
616 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000617 SkPaint newPaint(paint);
618 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619
reed@google.com40c2ba22011-07-25 19:41:22 +0000620 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000622
reed@google.com40c2ba22011-07-25 19:41:22 +0000623 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 SkPath path;
625 SkMatrix preMatrix;
reed@google.coma76de3d2011-01-13 18:30:42 +0000626
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 path.addCircle(0, 0, radius);
628 for (size_t i = 0; i < count; i++) {
629 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
630 // pass true for the last point, since we can modify
631 // then path then
reed@android.comf2b98d62010-12-20 18:26:13 +0000632 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000633 fDevice->drawPath(*this, path, newPaint, &preMatrix,
reed@android.comf2b98d62010-12-20 18:26:13 +0000634 (count-1) == i);
635 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000636 this->drawPath(path, newPaint, &preMatrix,
637 (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000638 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 }
640 } else {
641 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 for (size_t i = 0; i < count; i++) {
644 r.fLeft = pts[i].fX - radius;
645 r.fTop = pts[i].fY - radius;
646 r.fRight = r.fLeft + width;
647 r.fBottom = r.fTop + width;
reed@android.comf2b98d62010-12-20 18:26:13 +0000648 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000649 fDevice->drawRect(*this, r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000650 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000651 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000652 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 }
654 }
655 break;
656 }
657 case SkCanvas::kLines_PointMode:
robertphillips@google.com629ab542012-11-28 17:18:11 +0000658#ifndef SK_DISABLE_DASHING_OPTIMIZATION
659 if (2 == count && NULL != paint.getPathEffect()) {
660 // most likely a dashed line - see if it is one of the ones
661 // we can accelerate
662 SkStrokeRec rec(paint);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000663 SkPathEffect::PointData pointData;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000664
665 SkPath path;
666 path.moveTo(pts[0]);
667 path.lineTo(pts[1]);
668
reed@google.com4bbdeac2013-01-24 21:03:11 +0000669 SkRect cullRect = SkRect::Make(fRC->getBounds());
skia.committer@gmail.com4024f322013-01-25 07:06:46 +0000670
reed@google.com4bbdeac2013-01-24 21:03:11 +0000671 if (paint.getPathEffect()->asPoints(&pointData, path, rec,
672 *fMatrix, &cullRect)) {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000673 // 'asPoints' managed to find some fast path
674
robertphillips@google.com629ab542012-11-28 17:18:11 +0000675 SkPaint newP(paint);
676 newP.setPathEffect(NULL);
robertphillips@google.com935ad022012-12-05 19:07:21 +0000677 newP.setStyle(SkPaint::kFill_Style);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000678
robertphillips@google.com6d875572012-12-17 18:56:29 +0000679 if (!pointData.fFirst.isEmpty()) {
680 if (fDevice) {
681 fDevice->drawPath(*this, pointData.fFirst, newP);
682 } else {
683 this->drawPath(pointData.fFirst, newP);
684 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000685 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000686
687 if (!pointData.fLast.isEmpty()) {
688 if (fDevice) {
689 fDevice->drawPath(*this, pointData.fLast, newP);
690 } else {
691 this->drawPath(pointData.fLast, newP);
692 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000693 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000694
695 if (pointData.fSize.fX == pointData.fSize.fY) {
696 // The rest of the dashed line can just be drawn as points
697 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
698
699 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
700 newP.setStrokeCap(SkPaint::kRound_Cap);
701 } else {
702 newP.setStrokeCap(SkPaint::kButt_Cap);
703 }
704
705 if (fDevice) {
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000706 fDevice->drawPoints(*this,
robertphillips@google.com6d875572012-12-17 18:56:29 +0000707 SkCanvas::kPoints_PointMode,
708 pointData.fNumPoints,
709 pointData.fPoints,
710 newP);
711 } else {
712 this->drawPoints(SkCanvas::kPoints_PointMode,
713 pointData.fNumPoints,
714 pointData.fPoints,
715 newP,
716 forceUseDevice);
717 }
718 break;
719 } else {
720 // The rest of the dashed line must be drawn as rects
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000721 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
robertphillips@google.com6d875572012-12-17 18:56:29 +0000722 pointData.fFlags));
723
724 SkRect r;
725
726 for (int i = 0; i < pointData.fNumPoints; ++i) {
727 r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
728 pointData.fPoints[i].fY - pointData.fSize.fY,
729 pointData.fPoints[i].fX + pointData.fSize.fX,
730 pointData.fPoints[i].fY + pointData.fSize.fY);
731 if (fDevice) {
732 fDevice->drawRect(*this, r, newP);
733 } else {
734 this->drawRect(r, newP);
735 }
736 }
737 }
738
robertphillips@google.com629ab542012-11-28 17:18:11 +0000739 break;
740 }
741 }
742#endif // DISABLE_DASHING_OPTIMIZATION
743 // couldn't take fast path so fall through!
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 case SkCanvas::kPolygon_PointMode: {
745 count -= 1;
746 SkPath path;
747 SkPaint p(paint);
748 p.setStyle(SkPaint::kStroke_Style);
749 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
750 for (size_t i = 0; i < count; i += inc) {
751 path.moveTo(pts[i]);
752 path.lineTo(pts[i+1]);
reed@android.comf2b98d62010-12-20 18:26:13 +0000753 if (fDevice) {
754 fDevice->drawPath(*this, path, p, NULL, true);
755 } else {
756 this->drawPath(path, p, NULL, true);
757 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 path.rewind();
759 }
760 break;
761 }
762 }
763 }
764}
765
reed@google.com761fb622011-04-04 18:58:05 +0000766static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
767 SkPoint* strokeSize) {
768 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
769 paint.getStrokeMiter() < SK_ScalarSqrt2) {
770 return false;
771 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000772
reed@google.com761fb622011-04-04 18:58:05 +0000773 SkASSERT(matrix.rectStaysRect());
774 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
775 matrix.mapVectors(strokeSize, &pt, 1);
reed@google.com61153382011-04-05 13:05:18 +0000776 strokeSize->fX = SkScalarAbs(strokeSize->fX);
777 strokeSize->fY = SkScalarAbs(strokeSize->fY);
reed@google.com761fb622011-04-04 18:58:05 +0000778 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000779}
780
reed@google.com62ab7ad2011-04-05 14:08:25 +0000781SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
782 const SkMatrix& matrix,
783 SkPoint* strokeSize) {
784 RectType rtype;
785 const SkScalar width = paint.getStrokeWidth();
786 const bool zeroWidth = (0 == width);
787 SkPaint::Style style = paint.getStyle();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000788
reed@google.com62ab7ad2011-04-05 14:08:25 +0000789 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
790 style = SkPaint::kFill_Style;
791 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000792
reed@google.com62ab7ad2011-04-05 14:08:25 +0000793 if (paint.getPathEffect() || paint.getMaskFilter() ||
794 paint.getRasterizer() || !matrix.rectStaysRect() ||
795 SkPaint::kStrokeAndFill_Style == style) {
796 rtype = kPath_RectType;
797 } else if (SkPaint::kFill_Style == style) {
798 rtype = kFill_RectType;
799 } else if (zeroWidth) {
800 rtype = kHair_RectType;
801 } else if (easy_rect_join(paint, matrix, strokeSize)) {
802 rtype = kStroke_RectType;
803 } else {
804 rtype = kPath_RectType;
805 }
806 return rtype;
807}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000808
reed@google.com73244152012-05-11 14:35:23 +0000809static const SkPoint* rect_points(const SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000810 return SkTCast<const SkPoint*>(&r);
reed@google.com73244152012-05-11 14:35:23 +0000811}
812
813static SkPoint* rect_points(SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000814 return SkTCast<SkPoint*>(&r);
reed@google.com40c2ba22011-07-25 19:41:22 +0000815}
816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000818 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819
820 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000821 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 return;
823 }
824
reed@google.com761fb622011-04-04 18:58:05 +0000825 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000826 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000827
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000828 if (kPath_RectType == rtype) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 SkPath tmp;
830 tmp.addRect(rect);
831 tmp.setFillType(SkPath::kWinding_FillType);
reed@android.com187d5592009-07-08 14:03:56 +0000832 this->drawPath(tmp, paint, NULL, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 return;
834 }
835
836 const SkMatrix& matrix = *fMatrix;
837 SkRect devRect;
838
839 // transform rect into devRect
reed@google.com73244152012-05-11 14:35:23 +0000840 matrix.mapPoints(rect_points(devRect), rect_points(rect), 2);
841 devRect.sort();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842
843 if (fBounder && !fBounder->doRect(devRect, paint)) {
844 return;
845 }
846
847 // look for the quick exit, before we build a blitter
reed@google.com1c028bd2013-08-28 15:23:19 +0000848 SkIRect ir;
849 devRect.roundOut(&ir);
850 if (paint.getStyle() != SkPaint::kFill_Style) {
851 // extra space for hairlines
852 ir.inset(-1, -1);
853 }
854 if (fRC->quickReject(ir)) {
855 return;
rmistry@google.come09d6f42013-08-27 18:53:41 +0000856 }
857
reed@google.com1c028bd2013-08-28 15:23:19 +0000858 SkDeviceLooper looper(*fBitmap, *fRC, ir, paint.isAntiAlias());
859 while (looper.next()) {
860 SkRect localDevRect;
861 looper.mapRect(&localDevRect, devRect);
862 SkMatrix localMatrix;
863 looper.mapMatrix(&localMatrix, matrix);
rmistry@google.come09d6f42013-08-27 18:53:41 +0000864
reed@google.com1c028bd2013-08-28 15:23:19 +0000865 SkAutoBlitterChoose blitterStorage(looper.getBitmap(), localMatrix,
866 paint);
867 const SkRasterClip& clip = looper.getRC();
868 SkBlitter* blitter = blitterStorage.get();
869
870 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
871 // case we are also hairline (if we've gotten to here), which devolves to
872 // effectively just kFill
873 switch (rtype) {
874 case kFill_RectType:
875 if (paint.isAntiAlias()) {
876 SkScan::AntiFillRect(localDevRect, clip, blitter);
877 } else {
878 SkScan::FillRect(localDevRect, clip, blitter);
879 }
880 break;
881 case kStroke_RectType:
882 if (paint.isAntiAlias()) {
883 SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
884 } else {
885 SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
886 }
887 break;
888 case kHair_RectType:
889 if (paint.isAntiAlias()) {
890 SkScan::AntiHairRect(localDevRect, clip, blitter);
891 } else {
892 SkScan::HairRect(localDevRect, clip, blitter);
893 }
894 break;
895 default:
896 SkDEBUGFAIL("bad rtype");
897 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 }
899}
900
901void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
902 if (srcM.fBounds.isEmpty()) {
903 return;
904 }
905
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000906 const SkMask* mask = &srcM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000908 SkMask dstM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 if (paint.getMaskFilter() &&
910 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) {
911 mask = &dstM;
bungeman@google.combf2ac7e2011-09-26 19:51:33 +0000912 } else {
913 dstM.fImage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000915 SkAutoMaskFreeImage ami(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916
917 if (fBounder && !fBounder->doIRect(mask->fBounds)) {
918 return;
919 }
920
reed@google.com045e62d2011-10-24 12:19:46 +0000921 SkAutoBlitterChoose blitterChooser(*fBitmap, *fMatrix, paint);
922 SkBlitter* blitter = blitterChooser.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923
reed@google.com045e62d2011-10-24 12:19:46 +0000924 SkAAClipBlitterWrapper wrapper;
925 const SkRegion* clipRgn;
926
927 if (fRC->isBW()) {
928 clipRgn = &fRC->bwRgn();
929 } else {
930 wrapper.init(*fRC, blitter);
931 clipRgn = &wrapper.getRgn();
932 blitter = wrapper.getBlitter();
933 }
934 blitter->blitMaskRegion(*mask, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935}
936
reed@android.comebdeeb82009-09-03 21:45:49 +0000937static SkScalar fast_len(const SkVector& vec) {
938 SkScalar x = SkScalarAbs(vec.fX);
939 SkScalar y = SkScalarAbs(vec.fY);
940 if (x < y) {
941 SkTSwap(x, y);
942 }
943 return x + SkScalarHalf(y);
944}
945
reed@google.comecadf992011-09-19 19:18:18 +0000946static bool xfermodeSupportsCoverageAsAlpha(SkXfermode* xfer) {
947 SkXfermode::Coeff dc;
948 if (!SkXfermode::AsCoeff(xfer, NULL, &dc)) {
949 return false;
950 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000951
reed@google.comecadf992011-09-19 19:18:18 +0000952 switch (dc) {
953 case SkXfermode::kOne_Coeff:
954 case SkXfermode::kISA_Coeff:
955 case SkXfermode::kISC_Coeff:
956 return true;
957 default:
958 return false;
959 }
960}
961
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000962bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
963 SkScalar* coverage) {
964 SkASSERT(strokeWidth > 0);
965 // We need to try to fake a thick-stroke with a modulated hairline.
reed@google.comecadf992011-09-19 19:18:18 +0000966
tomhudson@google.com8d430182011-06-06 19:11:19 +0000967 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000968 return false;
969 }
reed@google.comecadf992011-09-19 19:18:18 +0000970
reed@android.comebdeeb82009-09-03 21:45:49 +0000971 SkVector src[2], dst[2];
reed@google.comecadf992011-09-19 19:18:18 +0000972 src[0].set(strokeWidth, 0);
973 src[1].set(0, strokeWidth);
reed@android.comebdeeb82009-09-03 21:45:49 +0000974 matrix.mapVectors(dst, src, 2);
975 SkScalar len0 = fast_len(dst[0]);
976 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000977 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000978 if (NULL != coverage) {
979 *coverage = SkScalarAve(len0, len1);
980 }
reed@android.comebdeeb82009-09-03 21:45:49 +0000981 return true;
982 }
983 return false;
984}
985
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000986void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
987 SkDEBUGCODE(this->validate());
988
989 if (fRC->isEmpty()) {
990 return;
991 }
992
993 {
994 // TODO: Investigate optimizing these options. They are in the same
995 // order as SkDraw::drawPath, which handles each case. It may be
996 // that there is no way to optimize for these using the SkRRect path.
997 SkScalar coverage;
998 if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
999 goto DRAW_PATH;
1000 }
1001
1002 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
1003 goto DRAW_PATH;
1004 }
1005
1006 if (paint.getRasterizer()) {
1007 goto DRAW_PATH;
1008 }
1009 }
1010
1011 if (paint.getMaskFilter()) {
1012 // Transform the rrect into device space.
1013 SkRRect devRRect;
1014 if (rrect.transform(*fMatrix, &devRRect)) {
1015 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
1016 if (paint.getMaskFilter()->filterRRect(devRRect, *fMatrix, *fRC,
1017 fBounder, blitter.get(),
1018 SkPaint::kFill_Style)) {
1019 return; // filterRRect() called the blitter, so we're done
1020 }
1021 }
1022 }
1023
1024DRAW_PATH:
1025 // Now fall back to the default case of using a path.
1026 SkPath path;
1027 path.addRRect(rrect);
1028 this->drawPath(path, paint, NULL, true);
1029}
1030
reed@google.com32e5d972011-07-27 18:25:57 +00001031void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
reed@google.com126f7f52013-11-07 16:06:53 +00001032 const SkMatrix* prePathMatrix, bool pathIsMutable,
1033 bool drawCoverage) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001034 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035
1036 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001037 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 return;
1039 }
1040
1041 SkPath* pathPtr = (SkPath*)&origSrcPath;
1042 bool doFill = true;
1043 SkPath tmpPath;
1044 SkMatrix tmpMatrix;
1045 const SkMatrix* matrix = fMatrix;
1046
1047 if (prePathMatrix) {
reed@google.com32e5d972011-07-27 18:25:57 +00001048 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
1049 origPaint.getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +00001051
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 if (!pathIsMutable) {
1053 result = &tmpPath;
1054 pathIsMutable = true;
1055 }
1056 pathPtr->transform(*prePathMatrix, result);
1057 pathPtr = result;
1058 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001059 tmpMatrix.setConcat(*matrix, *prePathMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 matrix = &tmpMatrix;
1061 }
1062 }
1063 // at this point we're done with prePathMatrix
1064 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +00001065
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001066 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
reed@google.coma76de3d2011-01-13 18:30:42 +00001067
reed@google.comecadf992011-09-19 19:18:18 +00001068 {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001069 SkScalar coverage;
1070 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
1071 if (SK_Scalar1 == coverage) {
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001072 paint.writable()->setStrokeWidth(0);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001073 } else if (xfermodeSupportsCoverageAsAlpha(origPaint.getXfermode())) {
1074 U8CPU newAlpha;
1075#if 0
1076 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
1077 origPaint.getAlpha()));
1078#else
1079 // this is the old technique, which we preserve for now so
1080 // we don't change previous results (testing)
1081 // the new way seems fine, its just (a tiny bit) different
1082 int scale = (int)SkScalarMul(coverage, 256);
1083 newAlpha = origPaint.getAlpha() * scale >> 8;
1084#endif
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001085 SkPaint* writablePaint = paint.writable();
1086 writablePaint->setStrokeWidth(0);
1087 writablePaint->setAlpha(newAlpha);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001088 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 }
1090 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001091
reed@google.com32e5d972011-07-27 18:25:57 +00001092 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
reed@google.com4bbdeac2013-01-24 21:03:11 +00001093 SkRect cullRect;
1094 const SkRect* cullRectPtr = NULL;
1095 if (this->computeConservativeLocalClipBounds(&cullRect)) {
1096 cullRectPtr = &cullRect;
1097 }
1098 doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 pathPtr = &tmpPath;
1100 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001101
reed@google.com32e5d972011-07-27 18:25:57 +00001102 if (paint->getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 SkMask mask;
reed@google.com32e5d972011-07-27 18:25:57 +00001104 if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
reed@google.com045e62d2011-10-24 12:19:46 +00001105 &fRC->getBounds(), paint->getMaskFilter(), &mask,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
reed@google.com32e5d972011-07-27 18:25:57 +00001107 this->drawDevMask(mask, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 SkMask::FreeImage(mask.fImage);
1109 }
1110 return;
1111 }
1112
1113 // avoid possibly allocating a new path in transform if we can
1114 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1115
1116 // transform the path into device space
1117 pathPtr->transform(*matrix, devPathPtr);
1118
reed@google.com126f7f52013-11-07 16:06:53 +00001119 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, *paint, drawCoverage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001121 if (paint->getMaskFilter()) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001122 SkPaint::Style style = doFill ? SkPaint::kFill_Style :
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001123 SkPaint::kStroke_Style;
1124 if (paint->getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fRC,
1125 fBounder, blitter.get(),
1126 style)) {
1127 return; // filterPath() called the blitter, so we're done
1128 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 }
1130
reed@google.com32e5d972011-07-27 18:25:57 +00001131 if (fBounder && !fBounder->doPath(*devPathPtr, *paint, doFill)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 return;
1133 }
1134
reed@google.com045e62d2011-10-24 12:19:46 +00001135 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 if (doFill) {
reed@google.com32e5d972011-07-27 18:25:57 +00001137 if (paint->isAntiAlias()) {
reed@google.com045e62d2011-10-24 12:19:46 +00001138 proc = SkScan::AntiFillPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 } else {
reed@google.com045e62d2011-10-24 12:19:46 +00001140 proc = SkScan::FillPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 }
1142 } else { // hairline
reed@google.com32e5d972011-07-27 18:25:57 +00001143 if (paint->isAntiAlias()) {
reed@google.com045e62d2011-10-24 12:19:46 +00001144 proc = SkScan::AntiHairPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 } else {
reed@google.com045e62d2011-10-24 12:19:46 +00001146 proc = SkScan::HairPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 }
1148 }
reed@google.com045e62d2011-10-24 12:19:46 +00001149 proc(*devPathPtr, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150}
1151
reed@android.com0baf1932009-06-24 12:41:42 +00001152/** For the purposes of drawing bitmaps, if a matrix is "almost" translate
1153 go ahead and treat it as if it were, so that subsequent code can go fast.
1154 */
1155static bool just_translate(const SkMatrix& matrix, const SkBitmap& bitmap) {
reed@google.com070dcd82013-01-07 20:27:52 +00001156 unsigned bits = 0; // TODO: find a way to allow the caller to tell us to
1157 // respect filtering.
1158 return SkTreatAsSprite(matrix, bitmap.width(), bitmap.height(), bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159}
1160
1161void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
1162 const SkPaint& paint) const {
reed@google.com900ecf22014-02-20 20:55:37 +00001163 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164
reed@google.coma76de3d2011-01-13 18:30:42 +00001165 if (just_translate(*fMatrix, bitmap)) {
reed@google.come1ca7052013-12-17 19:22:07 +00001166 int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
1167 int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168
reed@google.coma641f3f2012-12-13 22:16:30 +00001169 SkAutoLockPixels alp(bitmap);
1170 if (!bitmap.readyToDraw()) {
1171 return;
1172 }
1173
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 SkMask mask;
1175 mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
1176 mask.fFormat = SkMask::kA8_Format;
scroggo@google.come5f48242013-02-25 21:47:41 +00001177 mask.fRowBytes = SkToU32(bitmap.rowBytes());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 mask.fImage = bitmap.getAddr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001179
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 this->drawDevMask(mask, paint);
1181 } else { // need to xform the bitmap first
1182 SkRect r;
1183 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001184
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 r.set(0, 0,
1186 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1187 fMatrix->mapRect(&r);
1188 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001189
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 // set the mask's bounds to the transformed bitmap-bounds,
1191 // clipped to the actual device
1192 {
1193 SkIRect devBounds;
1194 devBounds.set(0, 0, fBitmap->width(), fBitmap->height());
1195 // need intersect(l, t, r, b) on irect
1196 if (!mask.fBounds.intersect(devBounds)) {
1197 return;
1198 }
1199 }
reed@android.com543ed932009-04-24 12:43:40 +00001200
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 mask.fFormat = SkMask::kA8_Format;
1202 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001203 size_t size = mask.computeImageSize();
1204 if (0 == size) {
1205 // the mask is too big to allocated, draw nothing
1206 return;
1207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208
1209 // allocate (and clear) our temp buffer to hold the transformed bitmap
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 SkAutoMalloc storage(size);
1211 mask.fImage = (uint8_t*)storage.get();
1212 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001213
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1215 {
1216 SkBitmap device;
1217 device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
1218 mask.fBounds.height(), mask.fRowBytes);
1219 device.setPixels(mask.fImage);
reed@google.coma76de3d2011-01-13 18:30:42 +00001220
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 SkCanvas c(device);
1222 // need the unclipped top/left for the translate
1223 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1224 -SkIntToScalar(mask.fBounds.fTop));
1225 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001226
1227 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001228 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001229 SkPaint tmpPaint;
1230 tmpPaint.setFlags(paint.getFlags());
reed@google.com40c2ba22011-07-25 19:41:22 +00001231 SkAutoBitmapShaderInstall install(bitmap, tmpPaint);
reed@android.com3469c762009-02-24 19:03:20 +00001232 SkRect rr;
1233 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1234 SkIntToScalar(bitmap.height()));
reed@google.com40c2ba22011-07-25 19:41:22 +00001235 c.drawRect(rr, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 }
1237 this->drawDevMask(mask, paint);
1238 }
1239}
1240
reed@google.com045e62d2011-10-24 12:19:46 +00001241static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 const SkRect& srcR) {
1243 SkRect dstR;
1244 SkIRect devIR;
reed@google.coma76de3d2011-01-13 18:30:42 +00001245
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 m.mapRect(&dstR, srcR);
reed@google.coma76de3d2011-01-13 18:30:42 +00001247 dstR.roundOut(&devIR);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 return c.quickReject(devIR);
1249}
1250
reed@google.com045e62d2011-10-24 12:19:46 +00001251static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 int width, int height) {
1253 SkRect r;
1254 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1255 return clipped_out(matrix, clip, r);
1256}
1257
reed@google.com045e62d2011-10-24 12:19:46 +00001258static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y,
1259 const SkBitmap& bitmap) {
1260 return clip.isBW() ||
1261 clip.quickContains(x, y, x + bitmap.width(), y + bitmap.height());
1262}
1263
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed@google.com40c2ba22011-07-25 19:41:22 +00001265 const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001266 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267
1268 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001269 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001271 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 return;
1273 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001274
reed@google.com40c2ba22011-07-25 19:41:22 +00001275 SkPaint paint(origPaint);
1276 paint.setStyle(SkPaint::kFill_Style);
reed@google.coma76de3d2011-01-13 18:30:42 +00001277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 SkMatrix matrix;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001279 matrix.setConcat(*fMatrix, prematrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280
reed@google.com045e62d2011-10-24 12:19:46 +00001281 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 return;
1283 }
1284
reed@android.com218521e2010-02-09 13:06:29 +00001285 if (fBounder && just_translate(matrix, bitmap)) {
1286 SkIRect ir;
reed@google.come1ca7052013-12-17 19:22:07 +00001287 int32_t ix = SkScalarRoundToInt(matrix.getTranslateX());
1288 int32_t iy = SkScalarRoundToInt(matrix.getTranslateY());
reed@android.com218521e2010-02-09 13:06:29 +00001289 ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
1290 if (!fBounder->doIRect(ir)) {
1291 return;
1292 }
1293 }
1294
reed@google.com900ecf22014-02-20 20:55:37 +00001295 if (bitmap.colorType() != kAlpha_8_SkColorType &&
reed@android.com0baf1932009-06-24 12:41:42 +00001296 just_translate(matrix, bitmap)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001297 //
1298 // It is safe to call lock pixels now, since we know the matrix is
1299 // (more or less) identity.
1300 //
1301 SkAutoLockPixels alp(bitmap);
1302 if (!bitmap.readyToDraw()) {
1303 return;
1304 }
reed@google.come1ca7052013-12-17 19:22:07 +00001305 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1306 int iy = SkScalarRoundToInt(matrix.getTranslateY());
reed@google.com045e62d2011-10-24 12:19:46 +00001307 if (clipHandlesSprite(*fRC, ix, iy, bitmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001308 SkTBlitterAllocator allocator;
1309 // blitter will be owned by the allocator.
1310 SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
1311 ix, iy, &allocator);
reed@google.com045e62d2011-10-24 12:19:46 +00001312 if (blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +00001313 SkIRect ir;
1314 ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315
reed@google.com045e62d2011-10-24 12:19:46 +00001316 SkScan::FillIRect(ir, *fRC, blitter);
1317 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 // now make a temp draw on the stack, and use it
1323 //
1324 SkDraw draw(*this);
1325 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001326
reed@google.com900ecf22014-02-20 20:55:37 +00001327 if (bitmap.colorType() == kAlpha_8_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 draw.drawBitmapAsMask(bitmap, paint);
1329 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +00001330 SkAutoBitmapShaderInstall install(bitmap, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331
1332 SkRect r;
1333 r.set(0, 0, SkIntToScalar(bitmap.width()),
1334 SkIntToScalar(bitmap.height()));
reed@google.coma76de3d2011-01-13 18:30:42 +00001335 // is this ok if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001336 draw.drawRect(r, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 }
1338}
1339
1340void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y,
reed@google.com40c2ba22011-07-25 19:41:22 +00001341 const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001342 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001343
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001345 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001347 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 return;
1349 }
1350
1351 SkIRect bounds;
1352 bounds.set(x, y, x + bitmap.width(), y + bitmap.height());
1353
reed@google.com045e62d2011-10-24 12:19:46 +00001354 if (fRC->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 return; // nothing to draw
1356 }
1357
reed@google.com40c2ba22011-07-25 19:41:22 +00001358 SkPaint paint(origPaint);
1359 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360
reed@google.com045e62d2011-10-24 12:19:46 +00001361 if (NULL == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, bitmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001362 SkTBlitterAllocator allocator;
1363 // blitter will be owned by the allocator.
1364 SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
1365 x, y, &allocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366
1367 if (blitter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 if (fBounder && !fBounder->doIRect(bounds)) {
1369 return;
1370 }
1371
reed@google.com045e62d2011-10-24 12:19:46 +00001372 SkScan::FillIRect(bounds, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 return;
1374 }
1375 }
1376
reed@google.com40c2ba22011-07-25 19:41:22 +00001377 SkAutoBitmapShaderInstall install(bitmap, paint);
1378 const SkPaint& shaderPaint = install.paintWithShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379
1380 SkMatrix matrix;
1381 SkRect r;
1382
1383 // get a scalar version of our rect
1384 r.set(bounds);
1385
1386 // tell the shader our offset
1387 matrix.setTranslate(r.fLeft, r.fTop);
reed@google.com40c2ba22011-07-25 19:41:22 +00001388 shaderPaint.getShader()->setLocalMatrix(matrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00001389
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 SkDraw draw(*this);
1391 matrix.reset();
1392 draw.fMatrix = &matrix;
1393 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001394 // is this OK if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001395 draw.drawRect(r, shaderPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396}
1397
1398///////////////////////////////////////////////////////////////////////////////
1399
1400#include "SkScalerContext.h"
1401#include "SkGlyphCache.h"
reed@google.come6913762012-08-07 15:19:47 +00001402#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403#include "SkUtils.h"
1404
1405static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
1406 const char text[], size_t byteLength, SkVector* stopVector) {
1407 SkFixed x = 0, y = 0;
1408 const char* stop = text + byteLength;
1409
1410 SkAutoKern autokern;
reed@google.coma76de3d2011-01-13 18:30:42 +00001411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 while (text < stop) {
1413 // don't need x, y here, since all subpixel variants will have the
1414 // same advance
1415 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1416
1417 x += autokern.adjust(glyph) + glyph.fAdvanceX;
1418 y += glyph.fAdvanceY;
1419 }
1420 stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
1421
1422 SkASSERT(text == stop);
1423}
1424
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001425bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
1426 // hairline glyphs are fast enough so we don't need to cache them
1427 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
1428 return true;
1429 }
1430
1431 // we don't cache perspective
1432 if (ctm.hasPerspective()) {
1433 return true;
1434 }
1435
1436 SkMatrix textM;
1437 return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM));
1438}
1439
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
1441 SkScalar x, SkScalar y,
1442 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001443 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444
djsollen@google.com166e6532012-03-20 14:24:38 +00001445 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446
1447 SkMatrix matrix;
1448 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1449 matrix.postTranslate(x, y);
1450
1451 const SkPath* iterPath;
1452 SkScalar xpos, prevXPos = 0;
1453
reed@google.com7b4531f2012-08-07 15:53:00 +00001454 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001456 if (iterPath) {
1457 const SkPaint& pnt = iter.getPaint();
1458 if (fDevice) {
1459 fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
1460 } else {
1461 this->drawPath(*iterPath, pnt, &matrix, false);
1462 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001463 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 prevXPos = xpos;
1465 }
1466}
1467
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468// disable warning : local variable used without having been initialized
reed@google.coma76de3d2011-01-13 18:30:42 +00001469#if defined _WIN32 && _MSC_VER >= 1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470#pragma warning ( push )
1471#pragma warning ( disable : 4701 )
1472#endif
1473
1474//////////////////////////////////////////////////////////////////////////////
1475
1476static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001477 SkFixed fx, SkFixed fy,
1478 const SkGlyph& glyph) {
mike@reedtribe.org9fb00412014-01-06 03:02:37 +00001479 int left = SkFixedFloorToInt(fx);
1480 int top = SkFixedFloorToInt(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001482 SkASSERT(NULL == state.fBounder);
1483 SkASSERT((NULL == state.fClip && state.fAAClip) ||
1484 (state.fClip && NULL == state.fAAClip && state.fClip->isRect()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485
1486 left += glyph.fLeft;
1487 top += glyph.fTop;
1488
1489 int right = left + glyph.fWidth;
1490 int bottom = top + glyph.fHeight;
1491
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001492 SkMask mask;
1493 SkIRect storage;
1494 SkIRect* bounds = &mask.fBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495
tomhudson@google.com83a44462011-10-27 15:27:51 +00001496 mask.fBounds.set(left, top, right, bottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
tomhudson@google.com83a44462011-10-27 15:27:51 +00001498 // this extra test is worth it, assuming that most of the time it succeeds
1499 // since we can avoid writing to storage
1500 if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) {
1501 if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds))
1502 return;
1503 bounds = &storage;
1504 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001505
tomhudson@google.com83a44462011-10-27 15:27:51 +00001506 uint8_t* aa = (uint8_t*)glyph.fImage;
1507 if (NULL == aa) {
1508 aa = (uint8_t*)state.fCache->findImage(glyph);
1509 if (NULL == aa) {
1510 return; // can't rasterize glyph
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 }
tomhudson@google.com83a44462011-10-27 15:27:51 +00001512 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513
tomhudson@google.com83a44462011-10-27 15:27:51 +00001514 mask.fRowBytes = glyph.rowBytes();
1515 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1516 mask.fImage = aa;
reed@google.com5bdfb332013-05-02 18:55:44 +00001517 state.blitMask(mask, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518}
1519
1520static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001521 SkFixed fx, SkFixed fy,
tomhudson@google.com83a44462011-10-27 15:27:51 +00001522 const SkGlyph& glyph) {
mike@reedtribe.org9fb00412014-01-06 03:02:37 +00001523 int left = SkFixedFloorToInt(fx);
1524 int top = SkFixedFloorToInt(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001526 SkASSERT(!state.fClip->isRect());
1527 SkASSERT(NULL == state.fBounder);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528
1529 SkMask mask;
1530
1531 left += glyph.fLeft;
1532 top += glyph.fTop;
1533
1534 mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001535 SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536
tomhudson@google.com83a44462011-10-27 15:27:51 +00001537 if (!clipper.done()) {
1538 const SkIRect& cr = clipper.rect();
1539 const uint8_t* aa = (const uint8_t*)glyph.fImage;
1540 if (NULL == aa) {
1541 aa = (uint8_t*)state.fCache->findImage(glyph);
1542 if (NULL == aa) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001543 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544 }
tomhudson@google.com83a44462011-10-27 15:27:51 +00001545 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001546
tomhudson@google.com83a44462011-10-27 15:27:51 +00001547 mask.fRowBytes = glyph.rowBytes();
1548 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1549 mask.fImage = (uint8_t*)aa;
1550 do {
reed@google.com5bdfb332013-05-02 18:55:44 +00001551 state.blitMask(mask, cr);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001552 clipper.next();
1553 } while (!clipper.done());
1554 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555}
1556
1557static void D1G_Bounder(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001558 SkFixed fx, SkFixed fy,
tomhudson@google.com83a44462011-10-27 15:27:51 +00001559 const SkGlyph& glyph) {
mike@reedtribe.org9fb00412014-01-06 03:02:37 +00001560 int left = SkFixedFloorToInt(fx);
1561 int top = SkFixedFloorToInt(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001563
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564 SkMask mask;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001565
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 left += glyph.fLeft;
1567 top += glyph.fTop;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001568
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
1570 SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001571
tomhudson@google.com83a44462011-10-27 15:27:51 +00001572 if (!clipper.done()) {
1573 const SkIRect& cr = clipper.rect();
1574 const uint8_t* aa = (const uint8_t*)glyph.fImage;
1575 if (NULL == aa) {
1576 aa = (uint8_t*)state.fCache->findImage(glyph);
1577 if (NULL == aa) {
1578 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001580 }
1581
reed@android.comd055c1f2010-03-01 14:54:05 +00001582 // we need to pass the origin, which we approximate with our
1583 // (unadjusted) left,top coordinates (the caller called fixedfloor)
tomhudson@google.com83a44462011-10-27 15:27:51 +00001584 if (state.fBounder->doIRectGlyph(cr,
reed@android.comd055c1f2010-03-01 14:54:05 +00001585 left - glyph.fLeft,
1586 top - glyph.fTop, glyph)) {
tomhudson@google.com83a44462011-10-27 15:27:51 +00001587 mask.fRowBytes = glyph.rowBytes();
1588 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1589 mask.fImage = (uint8_t*)aa;
1590 do {
reed@google.com5bdfb332013-05-02 18:55:44 +00001591 state.blitMask(mask, cr);
tomhudson@google.com83a44462011-10-27 15:27:51 +00001592 clipper.next();
1593 } while (!clipper.done());
1594 }
1595 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596}
1597
reed@google.com045e62d2011-10-24 12:19:46 +00001598static void D1G_Bounder_AAClip(const SkDraw1Glyph& state,
1599 SkFixed fx, SkFixed fy,
1600 const SkGlyph& glyph) {
mike@reedtribe.org9fb00412014-01-06 03:02:37 +00001601 int left = SkFixedFloorToInt(fx);
1602 int top = SkFixedFloorToInt(fy);
reed@google.com045e62d2011-10-24 12:19:46 +00001603 SkIRect bounds;
1604 bounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
1605
1606 if (state.fBounder->doIRectGlyph(bounds, left, top, glyph)) {
1607 D1G_NoBounder_RectClip(state, fx, fy, glyph);
1608 }
1609}
1610
reed@google.comfd4236e2011-07-25 21:16:22 +00001611static bool hasCustomD1GProc(const SkDraw& draw) {
1612 return draw.fProcs && draw.fProcs->fD1GProc;
1613}
1614
1615static bool needsRasterTextBlit(const SkDraw& draw) {
1616 return !hasCustomD1GProc(draw);
1617}
1618
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
reed@google.com5bdfb332013-05-02 18:55:44 +00001620 SkGlyphCache* cache, const SkPaint& pnt) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 fDraw = draw;
bungeman@google.com2211b622012-01-13 15:02:58 +00001622 fBounder = draw->fBounder;
1623 fBlitter = blitter;
1624 fCache = cache;
reed@google.com5bdfb332013-05-02 18:55:44 +00001625 fPaint = &pnt;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626
bungeman@google.com94471032013-02-25 15:55:13 +00001627 if (cache->isSubpixel()) {
1628 fHalfSampleX = fHalfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
1629 } else {
1630 fHalfSampleX = fHalfSampleY = SK_FixedHalf;
1631 }
1632
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001633 if (hasCustomD1GProc(*draw)) {
reed@google.com045e62d2011-10-24 12:19:46 +00001634 // todo: fix this assumption about clips w/ custom
1635 fClip = draw->fClip;
1636 fClipBounds = fClip->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637 return draw->fProcs->fD1GProc;
1638 }
1639
reed@google.com045e62d2011-10-24 12:19:46 +00001640 if (draw->fRC->isBW()) {
1641 fAAClip = NULL;
1642 fClip = &draw->fRC->bwRgn();
1643 fClipBounds = fClip->getBounds();
1644 if (NULL == fBounder) {
1645 if (fClip->isRect()) {
1646 return D1G_NoBounder_RectClip;
1647 } else {
1648 return D1G_NoBounder_RgnClip;
1649 }
1650 } else {
1651 return D1G_Bounder;
1652 }
1653 } else { // aaclip
1654 fAAClip = &draw->fRC->aaRgn();
1655 fClip = NULL;
1656 fClipBounds = fAAClip->getBounds();
1657 if (NULL == fBounder) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658 return D1G_NoBounder_RectClip;
1659 } else {
reed@google.com045e62d2011-10-24 12:19:46 +00001660 return D1G_Bounder_AAClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662 }
1663}
1664
reed@google.com5bdfb332013-05-02 18:55:44 +00001665void SkDraw1Glyph::blitMaskAsSprite(const SkMask& mask) const {
1666 SkASSERT(SkMask::kARGB32_Format == mask.fFormat);
1667
1668 SkBitmap bm;
1669 bm.setConfig(SkBitmap::kARGB_8888_Config,
1670 mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes);
1671 bm.setPixels((SkPMColor*)mask.fImage);
1672
1673 fDraw->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), *fPaint);
1674}
1675
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676///////////////////////////////////////////////////////////////////////////////
1677
1678void SkDraw::drawText(const char text[], size_t byteLength,
1679 SkScalar x, SkScalar y, const SkPaint& paint) const {
1680 SkASSERT(byteLength == 0 || text != NULL);
1681
reed@android.comf2b98d62010-12-20 18:26:13 +00001682 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683
1684 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001685 if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 return;
1687 }
1688
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001689 // SkScalarRec doesn't currently have a way of representing hairline stroke and
1690 // will fill if its frame-width is 0.
reed@google.comed43dff2013-06-04 16:56:27 +00001691 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692 this->drawText_asPaths(text, byteLength, x, y, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693 return;
1694 }
1695
1696 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
1697
bungeman@google.com94471032013-02-25 15:55:13 +00001698 SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699 SkGlyphCache* cache = autoCache.getCache();
reed@google.coma76de3d2011-01-13 18:30:42 +00001700
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701 // transform our starting point
1702 {
1703 SkPoint loc;
bungeman@google.com94471032013-02-25 15:55:13 +00001704 fMatrix->mapXY(x, y, &loc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705 x = loc.fX;
1706 y = loc.fY;
1707 }
1708
1709 // need to measure first
1710 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
1711 SkVector stop;
1712
1713 measure_text(cache, glyphCacheProc, text, byteLength, &stop);
1714
1715 SkScalar stopX = stop.fX;
1716 SkScalar stopY = stop.fY;
1717
1718 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1719 stopX = SkScalarHalf(stopX);
1720 stopY = SkScalarHalf(stopY);
1721 }
1722 x -= stopX;
1723 y -= stopY;
1724 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001725
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726 const char* stop = text + byteLength;
1727
reed@google.com045e62d2011-10-24 12:19:46 +00001728 SkAAClipBlitter aaBlitter;
1729 SkAutoBlitterChoose blitterChooser;
1730 SkBlitter* blitter = NULL;
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001731 if (needsRasterTextBlit(*this)) {
bungeman@google.com94471032013-02-25 15:55:13 +00001732 blitterChooser.choose(*fBitmap, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +00001733 blitter = blitterChooser.get();
1734 if (fRC->isAA()) {
1735 aaBlitter.init(blitter, &fRC->aaRgn());
1736 blitter = &aaBlitter;
1737 }
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001738 }
1739
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740 SkAutoKern autokern;
bungeman@google.com52c748b2011-08-22 21:30:43 +00001741 SkDraw1Glyph d1g;
reed@google.com5bdfb332013-05-02 18:55:44 +00001742 SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743
bungeman@google.com94471032013-02-25 15:55:13 +00001744 SkFixed fxMask = ~0;
1745 SkFixed fyMask = ~0;
1746 if (cache->isSubpixel()) {
1747 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
1748 if (kX_SkAxisAlignment == baseline) {
1749 fyMask = 0;
1750 d1g.fHalfSampleY = SK_FixedHalf;
1751 } else if (kY_SkAxisAlignment == baseline) {
1752 fxMask = 0;
1753 d1g.fHalfSampleX = SK_FixedHalf;
1754 }
1755 }
1756
1757 SkFixed fx = SkScalarToFixed(x) + d1g.fHalfSampleX;
1758 SkFixed fy = SkScalarToFixed(y) + d1g.fHalfSampleY;
1759
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760 while (text < stop) {
bungeman@google.com2211b622012-01-13 15:02:58 +00001761 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762
1763 fx += autokern.adjust(glyph);
1764
1765 if (glyph.fWidth) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001766 proc(d1g, fx, fy, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767 }
jvanverth@google.comd830d132013-11-11 20:54:09 +00001768
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769 fx += glyph.fAdvanceX;
1770 fy += glyph.fAdvanceY;
1771 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772}
1773
1774// last parameter is interpreted as SkFixed [x, y]
1775// return the fixed position, which may be rounded or not by the caller
1776// e.g. subpixel doesn't round
1777typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
1778
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001779static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
1781}
1782
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001783static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
1785 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
1786}
1787
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001788static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
1790 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
1791}
1792
1793static AlignProc pick_align_proc(SkPaint::Align align) {
1794 static const AlignProc gProcs[] = {
1795 leftAlignProc, centerAlignProc, rightAlignProc
1796 };
reed@google.coma76de3d2011-01-13 18:30:42 +00001797
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
1799
1800 return gProcs[align];
1801}
1802
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001803typedef void (*AlignProc_scalar)(const SkPoint&, const SkGlyph&, SkPoint*);
1804
1805static void leftAlignProc_scalar(const SkPoint& loc, const SkGlyph& glyph, SkPoint* dst) {
1806 dst->set(loc.fX, loc.fY);
1807}
1808
1809static void centerAlignProc_scalar(const SkPoint& loc, const SkGlyph& glyph, SkPoint* dst) {
1810 dst->set(loc.fX - SkFixedToScalar(glyph.fAdvanceX >> 1),
1811 loc.fY - SkFixedToScalar(glyph.fAdvanceY >> 1));
1812}
1813
1814static void rightAlignProc_scalar(const SkPoint& loc, const SkGlyph& glyph, SkPoint* dst) {
1815 dst->set(loc.fX - SkFixedToScalar(glyph.fAdvanceX),
1816 loc.fY - SkFixedToScalar(glyph.fAdvanceY));
1817}
1818
1819static AlignProc_scalar pick_align_proc_scalar(SkPaint::Align align) {
1820 static const AlignProc_scalar gProcs[] = {
1821 leftAlignProc_scalar, centerAlignProc_scalar, rightAlignProc_scalar
1822 };
1823
1824 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
1825
1826 return gProcs[align];
1827}
1828
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829class TextMapState {
1830public:
1831 mutable SkPoint fLoc;
reed@google.coma76de3d2011-01-13 18:30:42 +00001832
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 TextMapState(const SkMatrix& matrix, SkScalar y)
1834 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
1835
1836 typedef void (*Proc)(const TextMapState&, const SkScalar pos[]);
reed@google.coma76de3d2011-01-13 18:30:42 +00001837
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 Proc pickProc(int scalarsPerPosition);
reed@google.coma76de3d2011-01-13 18:30:42 +00001839
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840private:
1841 const SkMatrix& fMatrix;
1842 SkMatrix::MapXYProc fProc;
1843 SkScalar fY; // ignored by MapXYProc
1844 // these are only used by Only... procs
1845 SkScalar fScaleX, fTransX, fTransformedY;
1846
1847 static void MapXProc(const TextMapState& state, const SkScalar pos[]) {
1848 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
1849 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001850
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 static void MapXYProc(const TextMapState& state, const SkScalar pos[]) {
1852 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
1853 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001854
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855 static void MapOnlyScaleXProc(const TextMapState& state,
1856 const SkScalar pos[]) {
1857 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
1858 state.fTransformedY);
1859 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001860
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861 static void MapOnlyTransXProc(const TextMapState& state,
1862 const SkScalar pos[]) {
1863 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
1864 }
1865};
1866
1867TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) {
1868 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
reed@google.coma76de3d2011-01-13 18:30:42 +00001869
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870 if (1 == scalarsPerPosition) {
1871 unsigned mtype = fMatrix.getType();
1872 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
1873 return MapXProc;
1874 } else {
1875 fScaleX = fMatrix.getScaleX();
1876 fTransX = fMatrix.getTranslateX();
1877 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
1878 fMatrix.getTranslateY();
1879 return (mtype & SkMatrix::kScale_Mask) ?
1880 MapOnlyScaleXProc : MapOnlyTransXProc;
1881 }
1882 } else {
1883 return MapXYProc;
1884 }
1885}
1886
1887//////////////////////////////////////////////////////////////////////////////
1888
reed@google.comed43dff2013-06-04 16:56:27 +00001889void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength,
1890 const SkScalar pos[], SkScalar constY,
1891 int scalarsPerPosition,
1892 const SkPaint& origPaint) const {
1893 // setup our std paint, in hopes of getting hits in the cache
1894 SkPaint paint(origPaint);
1895 SkScalar matrixScale = paint.setupForAsPaths();
1896
reed@google.com5a649022013-06-05 18:00:30 +00001897 SkMatrix matrix;
1898 matrix.setScale(matrixScale, matrixScale);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001899
reed@google.comed43dff2013-06-04 16:56:27 +00001900 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
1901 SkAutoGlyphCache autoCache(paint, NULL, NULL);
1902 SkGlyphCache* cache = autoCache.getCache();
1903
1904 const char* stop = text + byteLength;
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001905 AlignProc_scalar alignProc = pick_align_proc_scalar(paint.getTextAlign());
reed@google.comed43dff2013-06-04 16:56:27 +00001906 TextMapState tms(SkMatrix::I(), constY);
1907 TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001908
reed@google.comed43dff2013-06-04 16:56:27 +00001909 while (text < stop) {
1910 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1911 if (glyph.fWidth) {
1912 const SkPath* path = cache->findPath(glyph);
1913 if (path) {
1914 tmsProc(tms, pos);
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001915 SkPoint loc;
1916 alignProc(tms.fLoc, glyph, &loc);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001917
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001918 matrix[SkMatrix::kMTransX] = loc.fX;
1919 matrix[SkMatrix::kMTransY] = loc.fY;
reed@google.com5a649022013-06-05 18:00:30 +00001920 if (fDevice) {
1921 fDevice->drawPath(*this, *path, paint, &matrix, false);
1922 } else {
1923 this->drawPath(*path, paint, &matrix, false);
1924 }
reed@google.comed43dff2013-06-04 16:56:27 +00001925 }
1926 }
1927 pos += scalarsPerPosition;
1928 }
1929}
1930
reed@android.com8a1c16f2008-12-17 15:59:43 +00001931void SkDraw::drawPosText(const char text[], size_t byteLength,
1932 const SkScalar pos[], SkScalar constY,
1933 int scalarsPerPosition, const SkPaint& paint) const {
1934 SkASSERT(byteLength == 0 || text != NULL);
1935 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1936
reed@android.comf2b98d62010-12-20 18:26:13 +00001937 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938
1939 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001940 if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941 return;
1942 }
1943
reed@google.comed43dff2013-06-04 16:56:27 +00001944 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
1945 this->drawPosText_asPaths(text, byteLength, pos, constY,
1946 scalarsPerPosition, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 return;
1948 }
1949
1950 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
bungeman@google.com94471032013-02-25 15:55:13 +00001951 SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952 SkGlyphCache* cache = autoCache.getCache();
reed@google.coma76de3d2011-01-13 18:30:42 +00001953
reed@google.com045e62d2011-10-24 12:19:46 +00001954 SkAAClipBlitterWrapper wrapper;
1955 SkAutoBlitterChoose blitterChooser;
1956 SkBlitter* blitter = NULL;
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001957 if (needsRasterTextBlit(*this)) {
bungeman@google.com94471032013-02-25 15:55:13 +00001958 blitterChooser.choose(*fBitmap, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +00001959 blitter = blitterChooser.get();
1960 if (fRC->isAA()) {
1961 wrapper.init(*fRC, blitter);
1962 blitter = wrapper.getBlitter();
1963 }
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001964 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001965
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 const char* stop = text + byteLength;
1967 AlignProc alignProc = pick_align_proc(paint.getTextAlign());
bungeman@google.com2211b622012-01-13 15:02:58 +00001968 SkDraw1Glyph d1g;
reed@google.com5bdfb332013-05-02 18:55:44 +00001969 SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache, paint);
bungeman@google.com94471032013-02-25 15:55:13 +00001970 TextMapState tms(*fMatrix, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971 TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
1972
bungeman@google.com2211b622012-01-13 15:02:58 +00001973 if (cache->isSubpixel()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974 // maybe we should skip the rounding if linearText is set
bungeman@google.com94471032013-02-25 15:55:13 +00001975 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
1976
1977 SkFixed fxMask = ~0;
1978 SkFixed fyMask = ~0;
1979 if (kX_SkAxisAlignment == baseline) {
1980 fyMask = 0;
1981#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
1982 d1g.fHalfSampleY = SK_FixedHalf;
1983#endif
1984 } else if (kY_SkAxisAlignment == baseline) {
1985 fxMask = 0;
1986#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
1987 d1g.fHalfSampleX = SK_FixedHalf;
1988#endif
1989 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990
1991 if (SkPaint::kLeft_Align == paint.getTextAlign()) {
1992 while (text < stop) {
1993 tmsProc(tms, pos);
bungeman@google.com94471032013-02-25 15:55:13 +00001994 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + d1g.fHalfSampleX;
1995 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + d1g.fHalfSampleY;
reed@google.coma76de3d2011-01-13 18:30:42 +00001996
reed@android.comf2b98d62010-12-20 18:26:13 +00001997 const SkGlyph& glyph = glyphCacheProc(cache, &text,
1998 fx & fxMask, fy & fyMask);
reed@google.coma76de3d2011-01-13 18:30:42 +00001999
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000 if (glyph.fWidth) {
reed@android.comf2b98d62010-12-20 18:26:13 +00002001 proc(d1g, fx, fy, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002 }
2003 pos += scalarsPerPosition;
2004 }
2005 } else {
2006 while (text < stop) {
bungeman@google.com9330cfe2012-01-04 14:17:00 +00002007 const char* currentText = text;
bungeman@google.com94471032013-02-25 15:55:13 +00002008 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00002009
bungeman@google.com94471032013-02-25 15:55:13 +00002010 if (metricGlyph.fWidth) {
2011 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
2012 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002013
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014 tmsProc(tms, pos);
bungeman@google.com94471032013-02-25 15:55:13 +00002015 SkIPoint fixedLoc;
2016 alignProc(tms.fLoc, metricGlyph, &fixedLoc);
reed@google.coma76de3d2011-01-13 18:30:42 +00002017
bungeman@google.com94471032013-02-25 15:55:13 +00002018 SkFixed fx = fixedLoc.fX + d1g.fHalfSampleX;
2019 SkFixed fy = fixedLoc.fY + d1g.fHalfSampleY;
reed@google.coma76de3d2011-01-13 18:30:42 +00002020
reed@android.com8a1c16f2008-12-17 15:59:43 +00002021 // have to call again, now that we've been "aligned"
bungeman@google.com94471032013-02-25 15:55:13 +00002022 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
2023 fx & fxMask, fy & fyMask);
2024 // the assumption is that the metrics haven't changed
2025 SkASSERT(prevAdvX == glyph.fAdvanceX);
2026 SkASSERT(prevAdvY == glyph.fAdvanceY);
2027 SkASSERT(glyph.fWidth);
reed@google.coma76de3d2011-01-13 18:30:42 +00002028
bungeman@google.com94471032013-02-25 15:55:13 +00002029 proc(d1g, fx, fy, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030 }
2031 pos += scalarsPerPosition;
2032 }
2033 }
2034 } else { // not subpixel
reed@google.comaeb07862012-04-18 18:32:04 +00002035 if (SkPaint::kLeft_Align == paint.getTextAlign()) {
2036 while (text < stop) {
2037 // the last 2 parameters are ignored
2038 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002039
reed@google.comaeb07862012-04-18 18:32:04 +00002040 if (glyph.fWidth) {
2041 tmsProc(tms, pos);
reed@google.coma76de3d2011-01-13 18:30:42 +00002042
reed@google.comaeb07862012-04-18 18:32:04 +00002043 proc(d1g,
bungeman@google.com94471032013-02-25 15:55:13 +00002044 SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf, //d1g.fHalfSampleX,
2045 SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf, //d1g.fHalfSampleY,
reed@google.comaeb07862012-04-18 18:32:04 +00002046 glyph);
2047 }
2048 pos += scalarsPerPosition;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002049 }
reed@google.comaeb07862012-04-18 18:32:04 +00002050 } else {
2051 while (text < stop) {
2052 // the last 2 parameters are ignored
2053 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
2054
2055 if (glyph.fWidth) {
2056 tmsProc(tms, pos);
2057
2058 SkIPoint fixedLoc;
2059 alignProc(tms.fLoc, glyph, &fixedLoc);
2060
2061 proc(d1g,
bungeman@google.com94471032013-02-25 15:55:13 +00002062 fixedLoc.fX + SK_FixedHalf, //d1g.fHalfSampleX,
2063 fixedLoc.fY + SK_FixedHalf, //d1g.fHalfSampleY,
reed@google.comaeb07862012-04-18 18:32:04 +00002064 glyph);
2065 }
2066 pos += scalarsPerPosition;
2067 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068 }
2069 }
2070}
2071
2072#if defined _WIN32 && _MSC_VER >= 1300
2073#pragma warning ( pop )
2074#endif
2075
2076///////////////////////////////////////////////////////////////////////////////
2077
2078#include "SkPathMeasure.h"
2079
2080static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
2081 SkPathMeasure& meas, const SkMatrix& matrix) {
2082 SkMatrix::MapXYProc proc = matrix.getMapXYProc();
2083
2084 for (int i = 0; i < count; i++) {
2085 SkPoint pos;
2086 SkVector tangent;
2087
2088 proc(matrix, src[i].fX, src[i].fY, &pos);
2089 SkScalar sx = pos.fX;
2090 SkScalar sy = pos.fY;
2091
reed@google.com8f17b0d2012-04-12 13:24:30 +00002092 if (!meas.getPosTan(sx, &pos, &tangent)) {
2093 // set to 0 if the measure failed, so that we just set dst == pos
2094 tangent.set(0, 0);
2095 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096
2097 /* This is the old way (that explains our approach but is way too slow
2098 SkMatrix matrix;
2099 SkPoint pt;
2100
2101 pt.set(sx, sy);
2102 matrix.setSinCos(tangent.fY, tangent.fX);
2103 matrix.preTranslate(-sx, 0);
2104 matrix.postTranslate(pos.fX, pos.fY);
2105 matrix.mapPoints(&dst[i], &pt, 1);
2106 */
2107 dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
2108 pos.fY + SkScalarMul(tangent.fX, sy));
2109 }
2110}
2111
2112/* TODO
2113
2114 Need differentially more subdivisions when the follow-path is curvy. Not sure how to
2115 determine that, but we need it. I guess a cheap answer is let the caller tell us,
2116 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
2117*/
2118static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
2119 const SkMatrix& matrix) {
2120 SkPath::Iter iter(src, false);
2121 SkPoint srcP[4], dstP[3];
2122 SkPath::Verb verb;
2123
2124 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
2125 switch (verb) {
2126 case SkPath::kMove_Verb:
2127 morphpoints(dstP, srcP, 1, meas, matrix);
2128 dst->moveTo(dstP[0]);
2129 break;
2130 case SkPath::kLine_Verb:
2131 // turn lines into quads to look bendy
2132 srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
2133 srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
2134 morphpoints(dstP, srcP, 2, meas, matrix);
2135 dst->quadTo(dstP[0], dstP[1]);
2136 break;
2137 case SkPath::kQuad_Verb:
2138 morphpoints(dstP, &srcP[1], 2, meas, matrix);
2139 dst->quadTo(dstP[0], dstP[1]);
2140 break;
2141 case SkPath::kCubic_Verb:
2142 morphpoints(dstP, &srcP[1], 3, meas, matrix);
2143 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
2144 break;
2145 case SkPath::kClose_Verb:
2146 dst->close();
2147 break;
2148 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002149 SkDEBUGFAIL("unknown verb");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150 break;
2151 }
2152 }
2153}
2154
2155void SkDraw::drawTextOnPath(const char text[], size_t byteLength,
2156 const SkPath& follow, const SkMatrix* matrix,
2157 const SkPaint& paint) const {
2158 SkASSERT(byteLength == 0 || text != NULL);
2159
2160 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00002161 if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162 return;
2163 }
2164
djsollen@google.com166e6532012-03-20 14:24:38 +00002165 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166 SkPathMeasure meas(follow, false);
2167 SkScalar hOffset = 0;
2168
2169 // need to measure first
2170 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
2171 SkScalar pathLen = meas.getLength();
2172 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2173 pathLen = SkScalarHalf(pathLen);
2174 }
2175 hOffset += pathLen;
2176 }
2177
2178 const SkPath* iterPath;
2179 SkScalar xpos;
2180 SkMatrix scaledMatrix;
2181 SkScalar scale = iter.getPathScale();
2182
2183 scaledMatrix.setScale(scale, scale);
reed@google.coma76de3d2011-01-13 18:30:42 +00002184
reed@google.com7b4531f2012-08-07 15:53:00 +00002185 while (iter.next(&iterPath, &xpos)) {
2186 if (iterPath) {
2187 SkPath tmp;
2188 SkMatrix m(scaledMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189
reed@google.com7b4531f2012-08-07 15:53:00 +00002190 m.postTranslate(xpos + hOffset, 0);
2191 if (matrix) {
2192 m.postConcat(*matrix);
2193 }
2194 morphpath(&tmp, *iterPath, meas, m);
2195 if (fDevice) {
2196 fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true);
2197 } else {
2198 this->drawPath(tmp, iter.getPaint(), NULL, true);
2199 }
reed@android.comf2b98d62010-12-20 18:26:13 +00002200 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 }
2202}
2203
2204///////////////////////////////////////////////////////////////////////////////
2205
2206struct VertState {
2207 int f0, f1, f2;
2208
2209 VertState(int vCount, const uint16_t indices[], int indexCount)
2210 : fIndices(indices) {
2211 fCurrIndex = 0;
2212 if (indices) {
2213 fCount = indexCount;
2214 } else {
2215 fCount = vCount;
2216 }
2217 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002218
2219 typedef bool (*Proc)(VertState*);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220 Proc chooseProc(SkCanvas::VertexMode mode);
2221
2222private:
2223 int fCount;
2224 int fCurrIndex;
2225 const uint16_t* fIndices;
reed@google.coma76de3d2011-01-13 18:30:42 +00002226
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227 static bool Triangles(VertState*);
2228 static bool TrianglesX(VertState*);
2229 static bool TriangleStrip(VertState*);
2230 static bool TriangleStripX(VertState*);
2231 static bool TriangleFan(VertState*);
2232 static bool TriangleFanX(VertState*);
2233};
2234
2235bool VertState::Triangles(VertState* state) {
2236 int index = state->fCurrIndex;
2237 if (index + 3 > state->fCount) {
2238 return false;
2239 }
2240 state->f0 = index + 0;
2241 state->f1 = index + 1;
2242 state->f2 = index + 2;
2243 state->fCurrIndex = index + 3;
2244 return true;
2245}
2246
2247bool VertState::TrianglesX(VertState* state) {
2248 const uint16_t* indices = state->fIndices;
2249 int index = state->fCurrIndex;
2250 if (index + 3 > state->fCount) {
2251 return false;
2252 }
2253 state->f0 = indices[index + 0];
2254 state->f1 = indices[index + 1];
2255 state->f2 = indices[index + 2];
2256 state->fCurrIndex = index + 3;
2257 return true;
2258}
2259
2260bool VertState::TriangleStrip(VertState* state) {
2261 int index = state->fCurrIndex;
2262 if (index + 3 > state->fCount) {
2263 return false;
2264 }
2265 state->f2 = index + 2;
2266 if (index & 1) {
2267 state->f0 = index + 1;
2268 state->f1 = index + 0;
2269 } else {
2270 state->f0 = index + 0;
2271 state->f1 = index + 1;
2272 }
2273 state->fCurrIndex = index + 1;
2274 return true;
2275}
2276
2277bool VertState::TriangleStripX(VertState* state) {
2278 const uint16_t* indices = state->fIndices;
2279 int index = state->fCurrIndex;
2280 if (index + 3 > state->fCount) {
2281 return false;
2282 }
2283 state->f2 = indices[index + 2];
2284 if (index & 1) {
2285 state->f0 = indices[index + 1];
2286 state->f1 = indices[index + 0];
2287 } else {
2288 state->f0 = indices[index + 0];
2289 state->f1 = indices[index + 1];
2290 }
2291 state->fCurrIndex = index + 1;
2292 return true;
2293}
2294
2295bool VertState::TriangleFan(VertState* state) {
2296 int index = state->fCurrIndex;
2297 if (index + 3 > state->fCount) {
2298 return false;
2299 }
2300 state->f0 = 0;
2301 state->f1 = index + 1;
2302 state->f2 = index + 2;
2303 state->fCurrIndex = index + 1;
2304 return true;
2305}
2306
2307bool VertState::TriangleFanX(VertState* state) {
2308 const uint16_t* indices = state->fIndices;
2309 int index = state->fCurrIndex;
2310 if (index + 3 > state->fCount) {
2311 return false;
2312 }
2313 state->f0 = indices[0];
2314 state->f1 = indices[index + 1];
2315 state->f2 = indices[index + 2];
2316 state->fCurrIndex = index + 1;
2317 return true;
2318}
2319
2320VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) {
2321 switch (mode) {
2322 case SkCanvas::kTriangles_VertexMode:
2323 return fIndices ? TrianglesX : Triangles;
2324 case SkCanvas::kTriangleStrip_VertexMode:
2325 return fIndices ? TriangleStripX : TriangleStrip;
2326 case SkCanvas::kTriangleFan_VertexMode:
2327 return fIndices ? TriangleFanX : TriangleFan;
2328 default:
2329 return NULL;
2330 }
2331}
2332
reed@google.com045e62d2011-10-24 12:19:46 +00002333typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRasterClip&,
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334 SkBlitter*);
2335
2336static HairProc ChooseHairProc(bool doAntiAlias) {
2337 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
2338}
2339
2340static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
2341 const SkPoint texs[], SkMatrix* matrix) {
2342 SkPoint src[3], dst[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00002343
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 src[0] = texs[state.f0];
2345 src[1] = texs[state.f1];
2346 src[2] = texs[state.f2];
2347 dst[0] = verts[state.f0];
2348 dst[1] = verts[state.f1];
2349 dst[2] = verts[state.f2];
2350 return matrix->setPolyToPoly(src, dst, 3);
2351}
2352
2353class SkTriColorShader : public SkShader {
2354public:
2355 SkTriColorShader() {}
2356
2357 bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
reed@google.coma76de3d2011-01-13 18:30:42 +00002358
reed@google.coma641f3f2012-12-13 22:16:30 +00002359 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
reed@google.coma76de3d2011-01-13 18:30:42 +00002360
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002361 SK_TO_STRING_OVERRIDE()
djsollen@google.comba28d032012-03-26 17:57:35 +00002362 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTriColorShader)
2363
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364protected:
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002365 SkTriColorShader(SkReadBuffer& buffer) : SkShader(buffer) {}
reed@google.coma76de3d2011-01-13 18:30:42 +00002366
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367private:
2368 SkMatrix fDstToUnit;
2369 SkPMColor fColors[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00002370
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 typedef SkShader INHERITED;
2372};
2373
2374bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[],
2375 int index0, int index1, int index2) {
reed@google.coma76de3d2011-01-13 18:30:42 +00002376
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377 fColors[0] = SkPreMultiplyColor(colors[index0]);
2378 fColors[1] = SkPreMultiplyColor(colors[index1]);
2379 fColors[2] = SkPreMultiplyColor(colors[index2]);
reed@google.coma76de3d2011-01-13 18:30:42 +00002380
reed@android.com8a1c16f2008-12-17 15:59:43 +00002381 SkMatrix m, im;
2382 m.reset();
2383 m.set(0, pts[index1].fX - pts[index0].fX);
2384 m.set(1, pts[index2].fX - pts[index0].fX);
2385 m.set(2, pts[index0].fX);
2386 m.set(3, pts[index1].fY - pts[index0].fY);
2387 m.set(4, pts[index2].fY - pts[index0].fY);
2388 m.set(5, pts[index0].fY);
2389 if (!m.invert(&im)) {
2390 return false;
2391 }
commit-bot@chromium.org92362382014-03-18 12:51:48 +00002392 fDstToUnit.setConcat(im, this->getTotalInverse());
2393 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394}
2395
2396#include "SkColorPriv.h"
reed@android.com689411a2008-12-18 02:52:32 +00002397#include "SkComposeShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398
2399static int ScalarTo256(SkScalar v) {
2400 int scale = SkScalarToFixed(v) >> 8;
2401 if (scale < 0) {
2402 scale = 0;
2403 }
2404 if (scale > 255) {
2405 scale = 255;
2406 }
2407 return SkAlpha255To256(scale);
2408}
2409
2410void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
2411 SkPoint src;
reed@google.coma76de3d2011-01-13 18:30:42 +00002412
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413 for (int i = 0; i < count; i++) {
2414 fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
2415 x += 1;
reed@google.coma76de3d2011-01-13 18:30:42 +00002416
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417 int scale1 = ScalarTo256(src.fX);
2418 int scale2 = ScalarTo256(src.fY);
2419 int scale0 = 256 - scale1 - scale2;
2420 if (scale0 < 0) {
2421 if (scale1 > scale2) {
2422 scale2 = 256 - scale1;
2423 } else {
2424 scale1 = 256 - scale2;
2425 }
2426 scale0 = 0;
2427 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002428
reed@android.com8a1c16f2008-12-17 15:59:43 +00002429 dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
2430 SkAlphaMulQ(fColors[1], scale1) +
2431 SkAlphaMulQ(fColors[2], scale2);
2432 }
2433}
2434
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002435#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00002436void SkTriColorShader::toString(SkString* str) const {
2437 str->append("SkTriColorShader: (");
2438
2439 this->INHERITED::toString(str);
2440
2441 str->append(")");
2442}
2443#endif
2444
reed@android.com8a1c16f2008-12-17 15:59:43 +00002445void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
2446 const SkPoint vertices[], const SkPoint textures[],
2447 const SkColor colors[], SkXfermode* xmode,
2448 const uint16_t indices[], int indexCount,
2449 const SkPaint& paint) const {
2450 SkASSERT(0 == count || NULL != vertices);
reed@google.coma76de3d2011-01-13 18:30:42 +00002451
reed@android.com8a1c16f2008-12-17 15:59:43 +00002452 // abort early if there is nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00002453 if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002454 return;
2455 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002456
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457 // transform out vertices into device coordinates
2458 SkAutoSTMalloc<16, SkPoint> storage(count);
2459 SkPoint* devVerts = storage.get();
2460 fMatrix->mapPoints(devVerts, vertices, count);
reed@google.coma76de3d2011-01-13 18:30:42 +00002461
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462 if (fBounder) {
2463 SkRect bounds;
2464 bounds.set(devVerts, count);
2465 if (!fBounder->doRect(bounds, paint)) {
2466 return;
2467 }
2468 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002469
reed@android.com8a1c16f2008-12-17 15:59:43 +00002470 /*
2471 We can draw the vertices in 1 of 4 ways:
2472
2473 - solid color (no shader/texture[], no colors[])
2474 - just colors (no shader/texture[], has colors[])
2475 - just texture (has shader/texture[], no colors[])
2476 - colors * texture (has shader/texture[], has colors[])
reed@google.coma76de3d2011-01-13 18:30:42 +00002477
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478 Thus for texture drawing, we need both texture[] and a shader.
2479 */
2480
2481 SkTriColorShader triShader; // must be above declaration of p
2482 SkPaint p(paint);
2483
2484 SkShader* shader = p.getShader();
2485 if (NULL == shader) {
2486 // if we have no shader, we ignore the texture coordinates
2487 textures = NULL;
2488 } else if (NULL == textures) {
2489 // if we don't have texture coordinates, ignore the shader
2490 p.setShader(NULL);
2491 shader = NULL;
2492 }
2493
2494 // setup the custom shader (if needed)
2495 if (NULL != colors) {
2496 if (NULL == textures) {
2497 // just colors (no texture)
reed@google.coma641f3f2012-12-13 22:16:30 +00002498 shader = p.setShader(&triShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002499 } else {
2500 // colors * texture
2501 SkASSERT(shader);
2502 bool releaseMode = false;
2503 if (NULL == xmode) {
reed@google.com8d3cd7a2013-01-30 21:36:11 +00002504 xmode = SkXfermode::Create(SkXfermode::kModulate_Mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 releaseMode = true;
2506 }
2507 SkShader* compose = SkNEW_ARGS(SkComposeShader,
2508 (&triShader, shader, xmode));
2509 p.setShader(compose)->unref();
2510 if (releaseMode) {
2511 xmode->unref();
2512 }
2513 }
2514 }
2515
2516 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p);
reed@google.comea033602012-12-14 13:13:55 +00002517 // important that we abort early, as below we may manipulate the shader
2518 // and that is only valid if the shader returned true from setContext.
2519 // If it returned false, then our blitter will be the NullBlitter.
2520 if (blitter->isNullBlitter()) {
2521 return;
2522 }
2523
reed@android.com8a1c16f2008-12-17 15:59:43 +00002524 // setup our state and function pointer for iterating triangles
2525 VertState state(count, indices, indexCount);
2526 VertState::Proc vertProc = state.chooseProc(vmode);
reed@google.coma76de3d2011-01-13 18:30:42 +00002527
reed@android.com8a1c16f2008-12-17 15:59:43 +00002528 if (NULL != textures || NULL != colors) {
bsalomon@google.comf94b3a42012-10-31 18:09:01 +00002529 SkMatrix tempM;
2530 SkMatrix savedLocalM;
2531 if (shader) {
2532 savedLocalM = shader->getLocalMatrix();
2533 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002534
djsollen@google.com19376b82013-02-05 17:00:02 +00002535 // setContext has already been called and verified to return true
2536 // by the constructor of SkAutoBlitterChoose
2537 bool prevContextSuccess = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002538 while (vertProc(&state)) {
2539 if (NULL != textures) {
2540 if (texture_to_matrix(state, vertices, textures, &tempM)) {
bsalomon@google.comf94b3a42012-10-31 18:09:01 +00002541 tempM.postConcat(savedLocalM);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002542 shader->setLocalMatrix(tempM);
djsollen@google.com19376b82013-02-05 17:00:02 +00002543 // Need to recall setContext since we changed the local matrix.
2544 // However, we also need to balance the calls this with a
2545 // call to endContext which requires tracking the result of
2546 // the previous call to setContext.
2547 if (prevContextSuccess) {
2548 shader->endContext();
2549 }
2550 prevContextSuccess = shader->setContext(*fBitmap, p, *fMatrix);
2551 if (!prevContextSuccess) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002552 continue;
2553 }
2554 }
2555 }
2556 if (NULL != colors) {
2557 if (!triShader.setup(vertices, colors,
2558 state.f0, state.f1, state.f2)) {
2559 continue;
2560 }
2561 }
reed@google.com045e62d2011-10-24 12:19:46 +00002562
2563 SkPoint tmp[] = {
2564 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
2565 };
2566 SkScan::FillTriangle(tmp, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002567 }
djsollen@google.com19376b82013-02-05 17:00:02 +00002568
reed@android.com8a1c16f2008-12-17 15:59:43 +00002569 // now restore the shader's original local matrix
2570 if (NULL != shader) {
bsalomon@google.comf94b3a42012-10-31 18:09:01 +00002571 shader->setLocalMatrix(savedLocalM);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002572 }
djsollen@google.com19376b82013-02-05 17:00:02 +00002573
2574 // If the final call to setContext fails we must make it suceed so that the
2575 // call to endContext in the destructor for SkAutoBlitterChoose is balanced.
2576 if (!prevContextSuccess) {
2577 prevContextSuccess = shader->setContext(*fBitmap, paint, SkMatrix::I());
2578 SkASSERT(prevContextSuccess);
2579 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002580 } else {
2581 // no colors[] and no texture
2582 HairProc hairProc = ChooseHairProc(paint.isAntiAlias());
reed@google.com045e62d2011-10-24 12:19:46 +00002583 const SkRasterClip& clip = *fRC;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002584 while (vertProc(&state)) {
reed@google.com045e62d2011-10-24 12:19:46 +00002585 hairProc(devVerts[state.f0], devVerts[state.f1], clip, blitter.get());
2586 hairProc(devVerts[state.f1], devVerts[state.f2], clip, blitter.get());
2587 hairProc(devVerts[state.f2], devVerts[state.f0], clip, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002588 }
2589 }
2590}
2591
reed@google.com0a0a2362011-03-23 13:51:55 +00002592///////////////////////////////////////////////////////////////////////////////
2593///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002594
2595#ifdef SK_DEBUG
2596
reed@android.comf2b98d62010-12-20 18:26:13 +00002597void SkDraw::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002598 SkASSERT(fBitmap != NULL);
2599 SkASSERT(fMatrix != NULL);
2600 SkASSERT(fClip != NULL);
reed@google.com045e62d2011-10-24 12:19:46 +00002601 SkASSERT(fRC != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002602
reed@google.com045e62d2011-10-24 12:19:46 +00002603 const SkIRect& cr = fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002604 SkIRect br;
2605
reed@android.comf2b98d62010-12-20 18:26:13 +00002606 br.set(0, 0, fBitmap->width(), fBitmap->height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002607 SkASSERT(cr.isEmpty() || br.contains(cr));
2608}
2609
2610#endif
2611
reed@google.com0a0a2362011-03-23 13:51:55 +00002612///////////////////////////////////////////////////////////////////////////////
2613
2614SkBounder::SkBounder() {
2615 // initialize up front. This gets reset by SkCanvas before each draw call.
2616 fClip = &SkRegion::GetEmptyRegion();
2617}
reed@android.com8a1c16f2008-12-17 15:59:43 +00002618
2619bool SkBounder::doIRect(const SkIRect& r) {
2620 SkIRect rr;
2621 return rr.intersect(fClip->getBounds(), r) && this->onIRect(rr);
2622}
2623
reed@android.comd055c1f2010-03-01 14:54:05 +00002624// TODO: change the prototype to take fixed, and update the callers
2625bool SkBounder::doIRectGlyph(const SkIRect& r, int x, int y,
2626 const SkGlyph& glyph) {
reed@android.com474a12c2010-01-04 19:35:33 +00002627 SkIRect rr;
reed@android.comd055c1f2010-03-01 14:54:05 +00002628 if (!rr.intersect(fClip->getBounds(), r)) {
2629 return false;
2630 }
2631 GlyphRec rec;
2632 rec.fLSB.set(SkIntToFixed(x), SkIntToFixed(y));
2633 rec.fRSB.set(rec.fLSB.fX + glyph.fAdvanceX,
2634 rec.fLSB.fY + glyph.fAdvanceY);
2635 rec.fGlyphID = glyph.getGlyphID();
2636 rec.fFlags = 0;
2637 return this->onIRectGlyph(rr, rec);
reed@android.com474a12c2010-01-04 19:35:33 +00002638}
2639
reed@android.com8a1c16f2008-12-17 15:59:43 +00002640bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1,
2641 const SkPaint& paint) {
2642 SkIRect r;
2643 SkScalar v0, v1;
2644
2645 v0 = pt0.fX;
2646 v1 = pt1.fX;
2647 if (v0 > v1) {
2648 SkTSwap<SkScalar>(v0, v1);
2649 }
reed@google.come1ca7052013-12-17 19:22:07 +00002650 r.fLeft = SkScalarFloorToInt(v0);
2651 r.fRight = SkScalarCeilToInt(v1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002652
2653 v0 = pt0.fY;
2654 v1 = pt1.fY;
2655 if (v0 > v1) {
2656 SkTSwap<SkScalar>(v0, v1);
2657 }
reed@google.come1ca7052013-12-17 19:22:07 +00002658 r.fTop = SkScalarFloorToInt(v0);
2659 r.fBottom = SkScalarCeilToInt(v1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002660
2661 if (paint.isAntiAlias()) {
2662 r.inset(-1, -1);
2663 }
2664 return this->doIRect(r);
2665}
2666
2667bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint) {
2668 SkIRect r;
2669
2670 if (paint.getStyle() == SkPaint::kFill_Style) {
2671 rect.round(&r);
2672 } else {
2673 int rad = -1;
2674 rect.roundOut(&r);
2675 if (paint.isAntiAlias()) {
2676 rad = -2;
2677 }
2678 r.inset(rad, rad);
2679 }
2680 return this->doIRect(r);
2681}
2682
2683bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) {
reed@android.comd252db02009-04-01 18:31:44 +00002684 SkIRect r;
2685 const SkRect& bounds = path.getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002686
2687 if (doFill) {
2688 bounds.round(&r);
2689 } else { // hairline
2690 bounds.roundOut(&r);
2691 }
2692
2693 if (paint.isAntiAlias()) {
2694 r.inset(-1, -1);
2695 }
2696 return this->doIRect(r);
2697}
2698
2699void SkBounder::commit() {
2700 // override in subclass
2701}
2702
2703////////////////////////////////////////////////////////////////////////////////////////////////
2704
2705#include "SkPath.h"
2706#include "SkDraw.h"
2707#include "SkRegion.h"
2708#include "SkBlitter.h"
2709
2710static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +00002711 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00002712 SkIRect* bounds) {
2713 if (devPath.isEmpty()) {
2714 return false;
2715 }
2716
reed@android.com8a1c16f2008-12-17 15:59:43 +00002717 // init our bounds from the path
2718 {
reed@android.comd252db02009-04-01 18:31:44 +00002719 SkRect pathBounds = devPath.getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002720 pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf);
2721 pathBounds.roundOut(bounds);
2722 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002723
tomhudson@google.com6db75fc2012-03-23 14:46:48 +00002724 SkIPoint margin = SkIPoint::Make(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002725 if (filter) {
2726 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00002727
bungeman@google.com5af16f82011-09-02 15:06:44 +00002728 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00002729
reed@android.com8a1c16f2008-12-17 15:59:43 +00002730 srcM.fBounds = *bounds;
2731 srcM.fFormat = SkMask::kA8_Format;
2732 srcM.fImage = NULL;
2733 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
2734 return false;
2735 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002736 }
2737
bungeman@google.com5af16f82011-09-02 15:06:44 +00002738 // (possibly) trim the bounds to reflect the clip
reed@android.com8a1c16f2008-12-17 15:59:43 +00002739 // (plus whatever slop the filter needs)
bungeman@google.com5af16f82011-09-02 15:06:44 +00002740 if (clipBounds) {
2741 SkIRect tmp = *clipBounds;
reed@android.com35555912009-03-16 18:46:55 +00002742 // Ugh. Guard against gigantic margins from wacky filters. Without this
2743 // check we can request arbitrary amounts of slop beyond our visible
2744 // clip, and bring down the renderer (at least on finite RAM machines
2745 // like handsets, etc.). Need to balance this invented value between
2746 // quality of large filters like blurs, and the corresponding memory
2747 // requests.
2748 static const int MAX_MARGIN = 128;
2749 tmp.inset(-SkMin32(margin.fX, MAX_MARGIN),
2750 -SkMin32(margin.fY, MAX_MARGIN));
bungeman@google.com5af16f82011-09-02 15:06:44 +00002751 if (!bounds->intersect(tmp)) {
2752 return false;
2753 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002754 }
2755
2756 return true;
2757}
2758
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002759static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
2760 SkPaint::Style style) {
reed@google.com045e62d2011-10-24 12:19:46 +00002761 SkBitmap bm;
2762 SkDraw draw;
2763 SkRasterClip clip;
2764 SkMatrix matrix;
2765 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002766
2767 bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes);
2768 bm.setPixels(mask.fImage);
2769
reed@google.com045e62d2011-10-24 12:19:46 +00002770 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002771 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
2772 -SkIntToScalar(mask.fBounds.fTop));
2773
2774 draw.fBitmap = &bm;
reed@google.com045e62d2011-10-24 12:19:46 +00002775 draw.fRC = &clip;
2776 draw.fClip = &clip.bwRgn();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002777 draw.fMatrix = &matrix;
2778 draw.fBounder = NULL;
2779 paint.setAntiAlias(true);
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002780 paint.setStyle(style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002781 draw.drawPath(devPath, paint);
2782}
2783
2784bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +00002785 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002786 SkMask* mask, SkMask::CreateMode mode,
2787 SkPaint::Style style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002788 if (SkMask::kJustRenderImage_CreateMode != mode) {
2789 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
2790 return false;
2791 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002792
reed@android.com8a1c16f2008-12-17 15:59:43 +00002793 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
2794 mask->fFormat = SkMask::kA8_Format;
2795 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00002796 size_t size = mask->computeImageSize();
2797 if (0 == size) {
2798 // we're too big to allocate the mask, abort
2799 return false;
2800 }
2801 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002802 memset(mask->fImage, 0, mask->computeImageSize());
2803 }
2804
2805 if (SkMask::kJustComputeBounds_CreateMode != mode) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002806 draw_into_mask(*mask, devPath, style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002807 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002808
reed@android.com8a1c16f2008-12-17 15:59:43 +00002809 return true;
2810}