blob: bf91aa15b54dd257775120503bf91a12d690c92b [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 */
Mike Reed787a16d2017-05-15 09:29:18 -04007
bungeman903dcb02015-03-16 13:00:09 -04008#define __STDC_LIMIT_MACROS
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
Herb Derby83e939b2017-02-07 14:25:11 -050010#include "SkArenaAlloc.h"
Mike Reed787a16d2017-05-15 09:29:18 -040011#include "SkAutoBlitterChoose.h"
reed374772b2016-10-05 17:33:02 -070012#include "SkBlendModePriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkBlitter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkCanvas.h"
Cary Clarka4083c92017-09-15 11:59:23 -040015#include "SkColorData.h"
Mike Reed986480a2017-01-13 22:43:16 +000016#include "SkDevice.h"
reed@google.com1c028bd2013-08-28 15:23:19 +000017#include "SkDeviceLooper.h"
Mike Reed787a16d2017-05-15 09:29:18 -040018#include "SkDraw.h"
19#include "SkDrawProcs.h"
herbe5911c92015-11-09 13:15:21 -080020#include "SkFindAndPlaceGlyph.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkMaskFilter.h"
herbf553e4e2015-11-09 08:51:56 -080022#include "SkMatrix.h"
Mike Reed787a16d2017-05-15 09:29:18 -040023#include "SkMatrixUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPaint.h"
25#include "SkPathEffect.h"
reed@google.com045e62d2011-10-24 12:19:46 +000026#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkRasterizer.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000028#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkScan.h"
30#include "SkShader.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +000031#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000032#include "SkStroke.h"
halcanary435657f2015-09-15 12:53:07 -070033#include "SkStrokeRec.h"
herbf553e4e2015-11-09 08:51:56 -080034#include "SkTemplates.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070035#include "SkTextMapStateProc.h"
reed@google.com32e5d972011-07-27 18:25:57 +000036#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000037#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
Herb Derby4bf560a2017-02-14 11:13:26 -050039static SkPaint make_paint_with_image(
40 const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) {
41 SkPaint paint(origPaint);
42 paint.setShader(SkMakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
43 SkShader::kClamp_TileMode, matrix,
44 kNever_SkCopyPixelsMode));
45 return paint;
46}
reed@android.com8a1c16f2008-12-17 15:59:43 +000047
reed@android.com8a1c16f2008-12-17 15:59:43 +000048///////////////////////////////////////////////////////////////////////////////
49
reed@android.comf2b98d62010-12-20 18:26:13 +000050SkDraw::SkDraw() {
51 sk_bzero(this, sizeof(*this));
52}
53
reed@google.com4bbdeac2013-01-24 21:03:11 +000054bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
55 if (fRC->isEmpty()) {
56 return false;
57 }
58
59 SkMatrix inverse;
60 if (!fMatrix->invert(&inverse)) {
61 return false;
62 }
63
64 SkIRect devBounds = fRC->getBounds();
65 // outset to have slop for antialasing and hairlines
66 devBounds.outset(1, 1);
67 inverse.mapRect(localBounds, SkRect::Make(devBounds));
68 return true;
69}
70
reed@android.com8a1c16f2008-12-17 15:59:43 +000071///////////////////////////////////////////////////////////////////////////////
72
73typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
74
75static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +000076 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +000077}
78
79static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
80
81static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +000082 sk_memset32((uint32_t*)pixels, data, SkToInt(bytes >> 2));
reed@android.com8a1c16f2008-12-17 15:59:43 +000083}
84
85static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +000086 sk_memset16((uint16_t*)pixels, data, SkToInt(bytes >> 1));
reed@android.com8a1c16f2008-12-17 15:59:43 +000087}
88
89static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
90 memset(pixels, data, bytes);
91}
92
reed41e010c2015-06-09 12:16:53 -070093static BitmapXferProc ChooseBitmapXferProc(const SkPixmap& dst, const SkPaint& paint,
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 uint32_t* data) {
95 // todo: we can apply colorfilter up front if no shader, so we wouldn't
96 // need to abort this fastpath
Mike Klein555ea8e2017-05-25 08:58:43 -040097 if (paint.getShader() || paint.getColorFilter() || dst.colorSpace()) {
halcanary96fcdcc2015-08-27 07:41:13 -070098 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 }
100
reed374772b2016-10-05 17:33:02 -0700101 SkBlendMode mode = paint.getBlendMode();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000103
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 // collaps modes based on color...
reed374772b2016-10-05 17:33:02 -0700105 if (SkBlendMode::kSrcOver == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 unsigned alpha = SkColorGetA(color);
107 if (0 == alpha) {
reed374772b2016-10-05 17:33:02 -0700108 mode = SkBlendMode::kDst;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 } else if (0xFF == alpha) {
reed374772b2016-10-05 17:33:02 -0700110 mode = SkBlendMode::kSrc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 }
112 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 switch (mode) {
reed374772b2016-10-05 17:33:02 -0700115 case SkBlendMode::kClear:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116// SkDebugf("--- D_Clear_BitmapXferProc\n");
117 return D_Clear_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700118 case SkBlendMode::kDst:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119// SkDebugf("--- D_Dst_BitmapXferProc\n");
120 return D_Dst_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700121 case SkBlendMode::kSrc: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000123 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 */
125 SkPMColor pmc = SkPreMultiplyColor(color);
reed41e010c2015-06-09 12:16:53 -0700126 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000127 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 if (data) {
129 *data = pmc;
130 }
131// SkDebugf("--- D32_Src_BitmapXferProc\n");
132 return D32_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000133 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 if (data) {
135 *data = SkPixel32ToPixel16(pmc);
136 }
137// SkDebugf("--- D16_Src_BitmapXferProc\n");
138 return D16_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000139 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 if (data) {
141 *data = SkGetPackedA32(pmc);
142 }
143// SkDebugf("--- DA8_Src_BitmapXferProc\n");
144 return DA8_Src_BitmapXferProc;
145 default:
146 break;
147 }
148 break;
149 }
150 default:
151 break;
152 }
halcanary96fcdcc2015-08-27 07:41:13 -0700153 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154}
155
reed41e010c2015-06-09 12:16:53 -0700156static void CallBitmapXferProc(const SkPixmap& dst, const SkIRect& rect, BitmapXferProc proc,
157 uint32_t procData) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 int shiftPerPixel;
reed41e010c2015-06-09 12:16:53 -0700159 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000160 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 shiftPerPixel = 2;
162 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000163 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 shiftPerPixel = 1;
165 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000166 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 shiftPerPixel = 0;
168 break;
169 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000170 SkDEBUGFAIL("Can't use xferproc on this config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 return;
172 }
173
reed41e010c2015-06-09 12:16:53 -0700174 uint8_t* pixels = (uint8_t*)dst.writable_addr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 SkASSERT(pixels);
reed41e010c2015-06-09 12:16:53 -0700176 const size_t rowBytes = dst.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 const int widthBytes = rect.width() << shiftPerPixel;
178
179 // skip down to the first scanline and X position
180 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
181 for (int scans = rect.height() - 1; scans >= 0; --scans) {
182 proc(pixels, widthBytes, procData);
183 pixels += rowBytes;
184 }
185}
186
187void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000188 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189
reed@google.com045e62d2011-10-24 12:19:46 +0000190 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 return;
192 }
193
194 SkIRect devRect;
reed41e010c2015-06-09 12:16:53 -0700195 devRect.set(0, 0, fDst.width(), fDst.height());
reed@google.coma76de3d2011-01-13 18:30:42 +0000196
reed@google.com045e62d2011-10-24 12:19:46 +0000197 if (fRC->isBW()) {
198 /* If we don't have a shader (i.e. we're just a solid color) we may
199 be faster to operate directly on the device bitmap, rather than invoking
200 a blitter. Esp. true for xfermodes, which require a colorshader to be
201 present, which is just redundant work. Since we're drawing everywhere
202 in the clip, we don't have to worry about antialiasing.
203 */
204 uint32_t procData = 0; // to avoid the warning
reed41e010c2015-06-09 12:16:53 -0700205 BitmapXferProc proc = ChooseBitmapXferProc(fDst, paint, &procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000206 if (proc) {
207 if (D_Dst_BitmapXferProc == proc) { // nothing to do
208 return;
209 }
210
211 SkRegion::Iterator iter(fRC->bwRgn());
212 while (!iter.done()) {
reed41e010c2015-06-09 12:16:53 -0700213 CallBitmapXferProc(fDst, iter.rect(), proc, procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000214 iter.next();
215 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 return;
217 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 }
reed@google.com045e62d2011-10-24 12:19:46 +0000219
220 // normal case: use a blitter
reed41e010c2015-06-09 12:16:53 -0700221 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000222 SkScan::FillIRect(devRect, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223}
224
225///////////////////////////////////////////////////////////////////////////////
226
227struct PtProcRec {
228 SkCanvas::PointMode fMode;
229 const SkPaint* fPaint;
230 const SkRegion* fClip;
reed@google.com045e62d2011-10-24 12:19:46 +0000231 const SkRasterClip* fRC;
reed@google.coma76de3d2011-01-13 18:30:42 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 // computed values
234 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
237 SkBlitter*);
238
239 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000240 const SkRasterClip*);
241 Proc chooseProc(SkBlitter** blitter);
242
243private:
244 SkAAClipBlitterWrapper fWrapper;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245};
246
247static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
248 int count, SkBlitter* blitter) {
249 SkASSERT(rec.fClip->isRect());
250 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000253 int x = SkScalarFloorToInt(devPts[i].fX);
254 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 if (r.contains(x, y)) {
256 blitter->blitH(x, y, 1);
257 }
258 }
259}
260
261static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
262 const SkPoint devPts[], int count,
263 SkBlitter* blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +0000264 SkASSERT(rec.fRC->isRect());
265 const SkIRect& r = rec.fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700267 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
268 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000269
reed41e010c2015-06-09 12:16:53 -0700270 uint16_t* addr = dst->writable_addr16(0, 0);
271 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000272
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000274 int x = SkScalarFloorToInt(devPts[i].fX);
275 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 if (r.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
278 }
279 }
280}
281
reed@google.com2d47a212012-11-29 21:01:00 +0000282static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
283 const SkPoint devPts[], int count,
284 SkBlitter* blitter) {
285 SkASSERT(rec.fRC->isRect());
286 const SkIRect& r = rec.fRC->getBounds();
287 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700288 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
289 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000290
reed41e010c2015-06-09 12:16:53 -0700291 SkPMColor* addr = dst->writable_addr32(0, 0);
292 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000293
reed@google.com2d47a212012-11-29 21:01:00 +0000294 for (int i = 0; i < count; i++) {
295 int x = SkScalarFloorToInt(devPts[i].fX);
296 int y = SkScalarFloorToInt(devPts[i].fY);
297 if (r.contains(x, y)) {
298 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
299 }
300 }
301}
302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
304 int count, SkBlitter* blitter) {
305 for (int i = 0; i < count; i++) {
reed@google.come1ca7052013-12-17 19:22:07 +0000306 int x = SkScalarFloorToInt(devPts[i].fX);
307 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 if (rec.fClip->contains(x, y)) {
309 blitter->blitH(x, y, 1);
310 }
311 }
312}
313
314static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
315 int count, SkBlitter* blitter) {
316 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700317 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 }
319}
320
321static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
322 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700323 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324}
325
326// aa versions
327
328static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
329 int count, SkBlitter* blitter) {
330 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700331 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 }
333}
334
335static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
336 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700337 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338}
339
340// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
341
342static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
343 int count, SkBlitter* blitter) {
344 const SkFixed radius = rec.fRadius;
345 for (int i = 0; i < count; i++) {
346 SkFixed x = SkScalarToFixed(devPts[i].fX);
347 SkFixed y = SkScalarToFixed(devPts[i].fY);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000348
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 SkXRect r;
350 r.fLeft = x - radius;
351 r.fTop = y - radius;
352 r.fRight = x + radius;
353 r.fBottom = y + radius;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000354
reed@google.com045e62d2011-10-24 12:19:46 +0000355 SkScan::FillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 }
357}
358
359static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
360 int count, SkBlitter* blitter) {
361 const SkFixed radius = rec.fRadius;
362 for (int i = 0; i < count; i++) {
363 SkFixed x = SkScalarToFixed(devPts[i].fX);
364 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000365
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 SkXRect r;
367 r.fLeft = x - radius;
368 r.fTop = y - radius;
369 r.fRight = x + radius;
370 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000371
reed@google.com045e62d2011-10-24 12:19:46 +0000372 SkScan::AntiFillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 }
374}
375
reed@android.comb4f404a2009-07-10 17:02:17 +0000376// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
reed@google.com045e62d2011-10-24 12:19:46 +0000378 const SkMatrix* matrix, const SkRasterClip* rc) {
ochang3ece53e2015-05-21 15:44:53 -0700379 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
380 return false;
381 }
382
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 if (paint.getPathEffect()) {
384 return false;
385 }
386 SkScalar width = paint.getStrokeWidth();
387 if (0 == width) {
388 fMode = mode;
389 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700390 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000391 fRC = rc;
reed@google.com2d47a212012-11-29 21:01:00 +0000392 fRadius = SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 return true;
394 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000395 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
robertphillips9f2251c2014-11-04 13:33:50 -0800396 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
398 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
399 if (SkScalarNearlyZero(sx - sy)) {
400 if (sx < 0) {
401 sx = -sx;
402 }
403
404 fMode = mode;
405 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700406 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000407 fRC = rc;
Mike Reeda99b6ce2017-02-04 11:04:26 -0500408 fRadius = SkScalarToFixed(width * sx) >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 return true;
410 }
411 }
412 return false;
413}
414
reed@google.com045e62d2011-10-24 12:19:46 +0000415PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700416 Proc proc = nullptr;
reed@google.coma76de3d2011-01-13 18:30:42 +0000417
reed@google.com045e62d2011-10-24 12:19:46 +0000418 SkBlitter* blitter = *blitterPtr;
419 if (fRC->isBW()) {
420 fClip = &fRC->bwRgn();
421 } else {
422 fWrapper.init(*fRC, blitter);
423 fClip = &fWrapper.getRgn();
424 blitter = fWrapper.getBlitter();
425 *blitterPtr = blitter;
426 }
427
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 // for our arrays
429 SkASSERT(0 == SkCanvas::kPoints_PointMode);
430 SkASSERT(1 == SkCanvas::kLines_PointMode);
431 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
432 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
433
reed@google.com2d47a212012-11-29 21:01:00 +0000434 if (fPaint->isAntiAlias()) {
435 if (0 == fPaint->getStrokeWidth()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 static const Proc gAAProcs[] = {
437 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
438 };
439 proc = gAAProcs[fMode];
reed@google.com2d47a212012-11-29 21:01:00 +0000440 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
441 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
442 proc = aa_square_proc;
443 }
444 } else { // BW
445 if (fRadius <= SK_FixedHalf) { // small radii and hairline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
447 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700448 const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
reed@google.com900ecf22014-02-20 20:55:37 +0000449 if (bm && kRGB_565_SkColorType == bm->colorType()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 proc = bw_pt_rect_16_hair_proc;
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000451 } else if (bm && kN32_SkColorType == bm->colorType()) {
reed@google.com2d47a212012-11-29 21:01:00 +0000452 proc = bw_pt_rect_32_hair_proc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 } else {
454 proc = bw_pt_rect_hair_proc;
455 }
456 } else {
457 static Proc gBWProcs[] = {
458 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
459 };
460 proc = gBWProcs[fMode];
461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 } else {
463 proc = bw_square_proc;
464 }
465 }
466 return proc;
467}
468
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469// each of these costs 8-bytes of stack space, so don't make it too large
470// must be even for lines/polygon to work
471#define MAX_DEV_PTS 32
472
473void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000474 const SkPoint pts[], const SkPaint& paint,
Mike Reed99330ba2017-02-22 11:01:08 -0500475 SkBaseDevice* device) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 // if we're in lines mode, force count to be even
477 if (SkCanvas::kLines_PointMode == mode) {
478 count &= ~(size_t)1;
479 }
480
481 if ((long)count <= 0) {
482 return;
483 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000484
halcanary96fcdcc2015-08-27 07:41:13 -0700485 SkASSERT(pts != nullptr);
reed@android.comf2b98d62010-12-20 18:26:13 +0000486 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000487
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000489 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 return;
491 }
492
493 PtProcRec rec;
Mike Reed99330ba2017-02-22 11:01:08 -0500494 if (!device && rec.init(mode, paint, fMatrix, fRC)) {
reed41e010c2015-06-09 12:16:53 -0700495 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496
497 SkPoint devPts[MAX_DEV_PTS];
498 const SkMatrix* matrix = fMatrix;
499 SkBlitter* bltr = blitter.get();
reed@google.com045e62d2011-10-24 12:19:46 +0000500 PtProcRec::Proc proc = rec.chooseProc(&bltr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 // we have to back up subsequent passes if we're in polygon mode
502 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 do {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000505 int n = SkToInt(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 if (n > MAX_DEV_PTS) {
507 n = MAX_DEV_PTS;
508 }
509 matrix->mapPoints(devPts, pts, n);
510 proc(rec, devPts, n, bltr);
511 pts += n - backup;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000512 SkASSERT(SkToInt(count) >= n);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 count -= n;
514 if (count > 0) {
515 count += backup;
516 }
517 } while (count != 0);
518 } else {
519 switch (mode) {
520 case SkCanvas::kPoints_PointMode: {
521 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000522 SkPaint newPaint(paint);
523 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524
reed@google.com40c2ba22011-07-25 19:41:22 +0000525 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000527
reed@google.com40c2ba22011-07-25 19:41:22 +0000528 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 SkPath path;
530 SkMatrix preMatrix;
mtklein7ef849d2014-11-24 09:11:45 -0800531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 path.addCircle(0, 0, radius);
533 for (size_t i = 0; i < count; i++) {
534 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
535 // pass true for the last point, since we can modify
536 // then path then
jvanverthb3eb6872014-10-24 07:12:51 -0700537 path.setIsVolatile((count-1) == i);
Mike Reed99330ba2017-02-22 11:01:08 -0500538 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500539 device->drawPath(path, newPaint, &preMatrix, (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000540 } else {
Mike Reeda1361362017-03-07 09:37:29 -0500541 this->drawPath(path, newPaint, &preMatrix, (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000542 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 }
544 } else {
545 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000546
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 for (size_t i = 0; i < count; i++) {
548 r.fLeft = pts[i].fX - radius;
549 r.fTop = pts[i].fY - radius;
550 r.fRight = r.fLeft + width;
551 r.fBottom = r.fTop + width;
Mike Reed99330ba2017-02-22 11:01:08 -0500552 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500553 device->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000554 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000555 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000556 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 }
558 }
559 break;
560 }
561 case SkCanvas::kLines_PointMode:
bsalomon49f085d2014-09-05 13:34:00 -0700562 if (2 == count && paint.getPathEffect()) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000563 // most likely a dashed line - see if it is one of the ones
564 // we can accelerate
565 SkStrokeRec rec(paint);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000566 SkPathEffect::PointData pointData;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000567
568 SkPath path;
569 path.moveTo(pts[0]);
570 path.lineTo(pts[1]);
571
reed@google.com4bbdeac2013-01-24 21:03:11 +0000572 SkRect cullRect = SkRect::Make(fRC->getBounds());
skia.committer@gmail.com4024f322013-01-25 07:06:46 +0000573
reed@google.com4bbdeac2013-01-24 21:03:11 +0000574 if (paint.getPathEffect()->asPoints(&pointData, path, rec,
575 *fMatrix, &cullRect)) {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000576 // 'asPoints' managed to find some fast path
577
robertphillips@google.com629ab542012-11-28 17:18:11 +0000578 SkPaint newP(paint);
halcanary96fcdcc2015-08-27 07:41:13 -0700579 newP.setPathEffect(nullptr);
robertphillips@google.com935ad022012-12-05 19:07:21 +0000580 newP.setStyle(SkPaint::kFill_Style);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000581
robertphillips@google.com6d875572012-12-17 18:56:29 +0000582 if (!pointData.fFirst.isEmpty()) {
Mike Reed99330ba2017-02-22 11:01:08 -0500583 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500584 device->drawPath(pointData.fFirst, newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000585 } else {
586 this->drawPath(pointData.fFirst, newP);
587 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000588 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000589
590 if (!pointData.fLast.isEmpty()) {
Mike Reed99330ba2017-02-22 11:01:08 -0500591 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500592 device->drawPath(pointData.fLast, newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000593 } else {
594 this->drawPath(pointData.fLast, newP);
595 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000596 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000597
598 if (pointData.fSize.fX == pointData.fSize.fY) {
599 // The rest of the dashed line can just be drawn as points
600 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
601
602 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
603 newP.setStrokeCap(SkPaint::kRound_Cap);
604 } else {
605 newP.setStrokeCap(SkPaint::kButt_Cap);
606 }
607
Mike Reed99330ba2017-02-22 11:01:08 -0500608 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500609 device->drawPoints(SkCanvas::kPoints_PointMode,
610 pointData.fNumPoints,
611 pointData.fPoints,
612 newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000613 } else {
614 this->drawPoints(SkCanvas::kPoints_PointMode,
615 pointData.fNumPoints,
616 pointData.fPoints,
617 newP,
Mike Reed99330ba2017-02-22 11:01:08 -0500618 device);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000619 }
620 break;
621 } else {
622 // The rest of the dashed line must be drawn as rects
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000623 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
robertphillips@google.com6d875572012-12-17 18:56:29 +0000624 pointData.fFlags));
625
626 SkRect r;
627
628 for (int i = 0; i < pointData.fNumPoints; ++i) {
629 r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
630 pointData.fPoints[i].fY - pointData.fSize.fY,
631 pointData.fPoints[i].fX + pointData.fSize.fX,
632 pointData.fPoints[i].fY + pointData.fSize.fY);
Mike Reed99330ba2017-02-22 11:01:08 -0500633 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500634 device->drawRect(r, newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000635 } else {
636 this->drawRect(r, newP);
637 }
638 }
639 }
640
robertphillips@google.com629ab542012-11-28 17:18:11 +0000641 break;
642 }
643 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000644 // couldn't take fast path so fall through!
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 case SkCanvas::kPolygon_PointMode: {
646 count -= 1;
647 SkPath path;
648 SkPaint p(paint);
649 p.setStyle(SkPaint::kStroke_Style);
650 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
jvanverthb3eb6872014-10-24 07:12:51 -0700651 path.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 for (size_t i = 0; i < count; i += inc) {
653 path.moveTo(pts[i]);
654 path.lineTo(pts[i+1]);
Mike Reed99330ba2017-02-22 11:01:08 -0500655 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500656 device->drawPath(path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000657 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700658 this->drawPath(path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000659 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 path.rewind();
661 }
662 break;
663 }
664 }
665 }
666}
667
fmalita1a178ca2015-01-15 06:01:23 -0800668static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
669 SkASSERT(matrix.rectStaysRect());
670 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
671
672 SkVector size;
673 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
674 matrix.mapVectors(&size, &pt, 1);
675 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
676}
677
reed@google.com761fb622011-04-04 18:58:05 +0000678static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
679 SkPoint* strokeSize) {
680 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
681 paint.getStrokeMiter() < SK_ScalarSqrt2) {
682 return false;
683 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000684
fmalita1a178ca2015-01-15 06:01:23 -0800685 *strokeSize = compute_stroke_size(paint, matrix);
reed@google.com761fb622011-04-04 18:58:05 +0000686 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000687}
688
reed@google.com62ab7ad2011-04-05 14:08:25 +0000689SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
690 const SkMatrix& matrix,
691 SkPoint* strokeSize) {
692 RectType rtype;
693 const SkScalar width = paint.getStrokeWidth();
694 const bool zeroWidth = (0 == width);
695 SkPaint::Style style = paint.getStyle();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000696
reed@google.com62ab7ad2011-04-05 14:08:25 +0000697 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
698 style = SkPaint::kFill_Style;
699 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000700
reed@google.com62ab7ad2011-04-05 14:08:25 +0000701 if (paint.getPathEffect() || paint.getMaskFilter() ||
702 paint.getRasterizer() || !matrix.rectStaysRect() ||
703 SkPaint::kStrokeAndFill_Style == style) {
704 rtype = kPath_RectType;
705 } else if (SkPaint::kFill_Style == style) {
706 rtype = kFill_RectType;
707 } else if (zeroWidth) {
708 rtype = kHair_RectType;
709 } else if (easy_rect_join(paint, matrix, strokeSize)) {
710 rtype = kStroke_RectType;
711 } else {
712 rtype = kPath_RectType;
713 }
714 return rtype;
715}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000716
reed@google.com73244152012-05-11 14:35:23 +0000717static const SkPoint* rect_points(const SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000718 return SkTCast<const SkPoint*>(&r);
reed@google.com73244152012-05-11 14:35:23 +0000719}
720
721static SkPoint* rect_points(SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000722 return SkTCast<SkPoint*>(&r);
reed@google.com40c2ba22011-07-25 19:41:22 +0000723}
724
reed03939122014-12-15 13:42:51 -0800725void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
726 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000727 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728
729 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000730 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 return;
732 }
733
reed03939122014-12-15 13:42:51 -0800734 const SkMatrix* matrix;
735 SkMatrix combinedMatrixStorage;
736 if (paintMatrix) {
737 SkASSERT(postPaintRect);
738 combinedMatrixStorage.setConcat(*fMatrix, *paintMatrix);
739 matrix = &combinedMatrixStorage;
740 } else {
741 SkASSERT(!postPaintRect);
742 matrix = fMatrix;
743 }
744
reed@google.com761fb622011-04-04 18:58:05 +0000745 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000746 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000747
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000748 if (kPath_RectType == rtype) {
reed03939122014-12-15 13:42:51 -0800749 SkDraw draw(*this);
750 if (paintMatrix) {
751 draw.fMatrix = matrix;
752 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 SkPath tmp;
reed03939122014-12-15 13:42:51 -0800754 tmp.addRect(prePaintRect);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 tmp.setFillType(SkPath::kWinding_FillType);
halcanary96fcdcc2015-08-27 07:41:13 -0700756 draw.drawPath(tmp, paint, nullptr, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 return;
758 }
759
reed03939122014-12-15 13:42:51 -0800760 SkRect devRect;
fmalita1a178ca2015-01-15 06:01:23 -0800761 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
762 // skip the paintMatrix when transforming the rect by the CTM
763 fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
reed@google.com73244152012-05-11 14:35:23 +0000764 devRect.sort();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 // look for the quick exit, before we build a blitter
fmalita1a178ca2015-01-15 06:01:23 -0800767 SkRect bbox = devRect;
reed@google.com1c028bd2013-08-28 15:23:19 +0000768 if (paint.getStyle() != SkPaint::kFill_Style) {
769 // extra space for hairlines
georgeb3eba472014-09-09 11:33:57 -0700770 if (paint.getStrokeWidth() == 0) {
fmalita1a178ca2015-01-15 06:01:23 -0800771 bbox.outset(1, 1);
georgeb3eba472014-09-09 11:33:57 -0700772 } else {
fmalita1a178ca2015-01-15 06:01:23 -0800773 // For kStroke_RectType, strokeSize is already computed.
774 const SkPoint& ssize = (kStroke_RectType == rtype)
775 ? strokeSize
776 : compute_stroke_size(paint, *fMatrix);
777 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
georgeb3eba472014-09-09 11:33:57 -0700778 }
reed@google.com1c028bd2013-08-28 15:23:19 +0000779 }
fmalita1a178ca2015-01-15 06:01:23 -0800780
781 SkIRect ir = bbox.roundOut();
reed@google.com1c028bd2013-08-28 15:23:19 +0000782 if (fRC->quickReject(ir)) {
783 return;
rmistry@google.come09d6f42013-08-27 18:53:41 +0000784 }
785
reed41e010c2015-06-09 12:16:53 -0700786 SkDeviceLooper looper(fDst, *fRC, ir, paint.isAntiAlias());
reed@google.com1c028bd2013-08-28 15:23:19 +0000787 while (looper.next()) {
788 SkRect localDevRect;
789 looper.mapRect(&localDevRect, devRect);
790 SkMatrix localMatrix;
reed03939122014-12-15 13:42:51 -0800791 looper.mapMatrix(&localMatrix, *matrix);
rmistry@google.come09d6f42013-08-27 18:53:41 +0000792
reed41e010c2015-06-09 12:16:53 -0700793 SkAutoBlitterChoose blitterStorage(looper.getPixmap(), localMatrix, paint);
reed@google.com1c028bd2013-08-28 15:23:19 +0000794 const SkRasterClip& clip = looper.getRC();
795 SkBlitter* blitter = blitterStorage.get();
796
797 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
798 // case we are also hairline (if we've gotten to here), which devolves to
799 // effectively just kFill
800 switch (rtype) {
801 case kFill_RectType:
802 if (paint.isAntiAlias()) {
803 SkScan::AntiFillRect(localDevRect, clip, blitter);
804 } else {
805 SkScan::FillRect(localDevRect, clip, blitter);
806 }
807 break;
808 case kStroke_RectType:
809 if (paint.isAntiAlias()) {
810 SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
811 } else {
812 SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
813 }
814 break;
815 case kHair_RectType:
816 if (paint.isAntiAlias()) {
817 SkScan::AntiHairRect(localDevRect, clip, blitter);
818 } else {
819 SkScan::HairRect(localDevRect, clip, blitter);
820 }
821 break;
822 default:
823 SkDEBUGFAIL("bad rtype");
824 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 }
826}
827
828void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
829 if (srcM.fBounds.isEmpty()) {
830 return;
831 }
832
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000833 const SkMask* mask = &srcM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000835 SkMask dstM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 if (paint.getMaskFilter() &&
robertphillipse80eb922015-12-17 11:33:12 -0800837 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 mask = &dstM;
839 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000840 SkAutoMaskFreeImage ami(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841
reed41e010c2015-06-09 12:16:53 -0700842 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000843 SkBlitter* blitter = blitterChooser.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844
reed@google.com045e62d2011-10-24 12:19:46 +0000845 SkAAClipBlitterWrapper wrapper;
846 const SkRegion* clipRgn;
847
848 if (fRC->isBW()) {
849 clipRgn = &fRC->bwRgn();
850 } else {
851 wrapper.init(*fRC, blitter);
852 clipRgn = &wrapper.getRgn();
853 blitter = wrapper.getBlitter();
854 }
855 blitter->blitMaskRegion(*mask, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856}
857
reed@android.comebdeeb82009-09-03 21:45:49 +0000858static SkScalar fast_len(const SkVector& vec) {
859 SkScalar x = SkScalarAbs(vec.fX);
860 SkScalar y = SkScalarAbs(vec.fY);
861 if (x < y) {
862 SkTSwap(x, y);
863 }
864 return x + SkScalarHalf(y);
865}
866
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000867bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
868 SkScalar* coverage) {
869 SkASSERT(strokeWidth > 0);
870 // We need to try to fake a thick-stroke with a modulated hairline.
reed@google.comecadf992011-09-19 19:18:18 +0000871
tomhudson@google.com8d430182011-06-06 19:11:19 +0000872 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000873 return false;
874 }
reed@google.comecadf992011-09-19 19:18:18 +0000875
reed@android.comebdeeb82009-09-03 21:45:49 +0000876 SkVector src[2], dst[2];
reed@google.comecadf992011-09-19 19:18:18 +0000877 src[0].set(strokeWidth, 0);
878 src[1].set(0, strokeWidth);
reed@android.comebdeeb82009-09-03 21:45:49 +0000879 matrix.mapVectors(dst, src, 2);
880 SkScalar len0 = fast_len(dst[0]);
881 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000882 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
bsalomon49f085d2014-09-05 13:34:00 -0700883 if (coverage) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000884 *coverage = SkScalarAve(len0, len1);
885 }
reed@android.comebdeeb82009-09-03 21:45:49 +0000886 return true;
887 }
888 return false;
889}
890
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000891void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
892 SkDEBUGCODE(this->validate());
893
894 if (fRC->isEmpty()) {
895 return;
896 }
897
898 {
899 // TODO: Investigate optimizing these options. They are in the same
900 // order as SkDraw::drawPath, which handles each case. It may be
901 // that there is no way to optimize for these using the SkRRect path.
902 SkScalar coverage;
903 if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
904 goto DRAW_PATH;
905 }
906
907 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
908 goto DRAW_PATH;
909 }
910
911 if (paint.getRasterizer()) {
912 goto DRAW_PATH;
913 }
914 }
915
916 if (paint.getMaskFilter()) {
917 // Transform the rrect into device space.
918 SkRRect devRRect;
919 if (rrect.transform(*fMatrix, &devRRect)) {
reed41e010c2015-06-09 12:16:53 -0700920 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
bsalomon055e1922016-05-06 07:22:58 -0700921 if (paint.getMaskFilter()->filterRRect(devRRect, *fMatrix, *fRC, blitter.get())) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000922 return; // filterRRect() called the blitter, so we're done
923 }
924 }
925 }
926
927DRAW_PATH:
928 // Now fall back to the default case of using a path.
929 SkPath path;
930 path.addRRect(rrect);
halcanary96fcdcc2015-08-27 07:41:13 -0700931 this->drawPath(path, paint, nullptr, true);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000932}
933
caryclark1a7eb262016-01-21 07:07:02 -0800934SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
reed05d90442015-02-12 13:35:52 -0800935 if (!matrix.hasPerspective()) {
936 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
937 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
938 if (SkScalarsAreFinite(sx, sy)) {
lsalzmana4b87042016-07-06 12:19:56 -0700939 SkScalar scale = SkTMax(sx, sy);
940 if (scale > 0) {
941 return scale;
942 }
reed05d90442015-02-12 13:35:52 -0800943 }
944 }
945 return 1;
946}
947
reed82595b62016-05-09 17:48:46 -0700948void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
949 SkBlitter* customBlitter, bool doFill) const {
reed01a2ff82016-07-18 13:22:55 -0700950 // Do a conservative quick-reject test, since a looper or other modifier may have moved us
951 // out of range.
952 if (!devPath.isInverseFillType()) {
953 // If we're a H or V line, our bounds will be empty. So we bloat here just so we don't
954 // appear empty to the intersects call. This also gives us slop in case we're antialiasing
955 SkRect pathBounds = devPath.getBounds().makeOutset(1, 1);
956
957 if (paint.getMaskFilter()) {
958 paint.getMaskFilter()->computeFastBounds(pathBounds, &pathBounds);
959
960 // Need to outset the path to work-around a bug in blurmaskfilter. When that is fixed
961 // we can remove this hack. See skbug.com/5542
962 pathBounds.outset(7, 7);
963 }
964
965 // Now compare against the clip's bounds
966 if (!SkRect::Make(fRC->getBounds()).intersects(pathBounds)) {
967 return;
968 }
969 }
970
reed82595b62016-05-09 17:48:46 -0700971 SkBlitter* blitter = nullptr;
972 SkAutoBlitterChoose blitterStorage;
973 if (nullptr == customBlitter) {
974 blitterStorage.choose(fDst, *fMatrix, paint, drawCoverage);
975 blitter = blitterStorage.get();
976 } else {
977 blitter = customBlitter;
978 }
979
980 if (paint.getMaskFilter()) {
981 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
982 : SkStrokeRec::kHairline_InitStyle;
983 if (paint.getMaskFilter()->filterPath(devPath, *fMatrix, *fRC, blitter, style)) {
984 return; // filterPath() called the blitter, so we're done
985 }
986 }
987
988 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
989 if (doFill) {
990 if (paint.isAntiAlias()) {
991 proc = SkScan::AntiFillPath;
992 } else {
993 proc = SkScan::FillPath;
994 }
995 } else { // hairline
996 if (paint.isAntiAlias()) {
997 switch (paint.getStrokeCap()) {
998 case SkPaint::kButt_Cap:
999 proc = SkScan::AntiHairPath;
1000 break;
1001 case SkPaint::kSquare_Cap:
1002 proc = SkScan::AntiHairSquarePath;
1003 break;
1004 case SkPaint::kRound_Cap:
1005 proc = SkScan::AntiHairRoundPath;
1006 break;
1007 default:
1008 proc SK_INIT_TO_AVOID_WARNING;
1009 SkDEBUGFAIL("unknown paint cap type");
1010 }
1011 } else {
1012 switch (paint.getStrokeCap()) {
1013 case SkPaint::kButt_Cap:
1014 proc = SkScan::HairPath;
1015 break;
1016 case SkPaint::kSquare_Cap:
1017 proc = SkScan::HairSquarePath;
1018 break;
1019 case SkPaint::kRound_Cap:
1020 proc = SkScan::HairRoundPath;
1021 break;
1022 default:
1023 proc SK_INIT_TO_AVOID_WARNING;
1024 SkDEBUGFAIL("unknown paint cap type");
1025 }
1026 }
1027 }
1028 proc(devPath, *fRC, blitter);
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,
krajcevski53f09592014-08-06 11:12:14 -07001033 bool drawCoverage, SkBlitter* customBlitter) 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;
jvanverthb3eb6872014-10-24 07:12:51 -07001046 tmpPath.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047
1048 if (prePathMatrix) {
reed@google.com32e5d972011-07-27 18:25:57 +00001049 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
1050 origPaint.getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +00001052
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 if (!pathIsMutable) {
1054 result = &tmpPath;
1055 pathIsMutable = true;
1056 }
1057 pathPtr->transform(*prePathMatrix, result);
1058 pathPtr = result;
1059 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001060 tmpMatrix.setConcat(*matrix, *prePathMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 matrix = &tmpMatrix;
1062 }
1063 }
1064 // at this point we're done with prePathMatrix
1065 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +00001066
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001067 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
reed@google.coma76de3d2011-01-13 18:30:42 +00001068
reed@google.comecadf992011-09-19 19:18:18 +00001069 {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001070 SkScalar coverage;
1071 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
1072 if (SK_Scalar1 == coverage) {
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001073 paint.writable()->setStrokeWidth(0);
reed374772b2016-10-05 17:33:02 -07001074 } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001075 U8CPU newAlpha;
1076#if 0
1077 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
1078 origPaint.getAlpha()));
1079#else
1080 // this is the old technique, which we preserve for now so
1081 // we don't change previous results (testing)
1082 // the new way seems fine, its just (a tiny bit) different
Mike Reeda99b6ce2017-02-04 11:04:26 -05001083 int scale = (int)(coverage * 256);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001084 newAlpha = origPaint.getAlpha() * scale >> 8;
1085#endif
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001086 SkPaint* writablePaint = paint.writable();
1087 writablePaint->setStrokeWidth(0);
1088 writablePaint->setAlpha(newAlpha);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001089 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 }
1091 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001092
reed@google.com32e5d972011-07-27 18:25:57 +00001093 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
reed@google.com4bbdeac2013-01-24 21:03:11 +00001094 SkRect cullRect;
halcanary96fcdcc2015-08-27 07:41:13 -07001095 const SkRect* cullRectPtr = nullptr;
reed@google.com4bbdeac2013-01-24 21:03:11 +00001096 if (this->computeConservativeLocalClipBounds(&cullRect)) {
1097 cullRectPtr = &cullRect;
1098 }
reed05d90442015-02-12 13:35:52 -08001099 doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr,
caryclark1a7eb262016-01-21 07:07:02 -08001100 ComputeResScaleForStroking(*fMatrix));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 pathPtr = &tmpPath;
1102 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001103
reed@google.com32e5d972011-07-27 18:25:57 +00001104 if (paint->getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 SkMask mask;
reed@google.com32e5d972011-07-27 18:25:57 +00001106 if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
reed@google.com045e62d2011-10-24 12:19:46 +00001107 &fRC->getBounds(), paint->getMaskFilter(), &mask,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
reed@google.com32e5d972011-07-27 18:25:57 +00001109 this->drawDevMask(mask, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 SkMask::FreeImage(mask.fImage);
1111 }
1112 return;
1113 }
1114
1115 // avoid possibly allocating a new path in transform if we can
1116 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1117
1118 // transform the path into device space
1119 pathPtr->transform(*matrix, devPathPtr);
1120
reed82595b62016-05-09 17:48:46 -07001121 this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122}
1123
reed82595b62016-05-09 17:48:46 -07001124void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {
reed@google.com900ecf22014-02-20 20:55:37 +00001125 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126
fmalitac7e211a2016-01-07 10:34:46 -08001127 if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) {
reed@google.come1ca7052013-12-17 19:22:07 +00001128 int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
1129 int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130
Mike Reed4edb5d22017-04-17 11:02:51 -04001131 SkPixmap pmap;
1132 if (!bitmap.peekPixels(&pmap)) {
reed@google.coma641f3f2012-12-13 22:16:30 +00001133 return;
1134 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 SkMask mask;
reed92fc2ae2015-05-22 08:06:21 -07001136 mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 mask.fFormat = SkMask::kA8_Format;
reed92fc2ae2015-05-22 08:06:21 -07001138 mask.fRowBytes = SkToU32(pmap.rowBytes());
1139 // fImage is typed as writable, but in this case it is used read-only
1140 mask.fImage = (uint8_t*)pmap.addr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001141
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 this->drawDevMask(mask, paint);
1143 } else { // need to xform the bitmap first
1144 SkRect r;
1145 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001146
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 r.set(0, 0,
1148 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1149 fMatrix->mapRect(&r);
1150 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001151
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 // set the mask's bounds to the transformed bitmap-bounds,
1153 // clipped to the actual device
1154 {
1155 SkIRect devBounds;
reed41e010c2015-06-09 12:16:53 -07001156 devBounds.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 // need intersect(l, t, r, b) on irect
1158 if (!mask.fBounds.intersect(devBounds)) {
1159 return;
1160 }
1161 }
reed@android.com543ed932009-04-24 12:43:40 +00001162
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163 mask.fFormat = SkMask::kA8_Format;
1164 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001165 size_t size = mask.computeImageSize();
1166 if (0 == size) {
1167 // the mask is too big to allocated, draw nothing
1168 return;
1169 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170
1171 // allocate (and clear) our temp buffer to hold the transformed bitmap
scroggo565901d2015-12-10 10:44:13 -08001172 SkAutoTMalloc<uint8_t> storage(size);
1173 mask.fImage = storage.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001175
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1177 {
1178 SkBitmap device;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001179 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
1180 mask.fImage, mask.fRowBytes);
reed@google.coma76de3d2011-01-13 18:30:42 +00001181
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 SkCanvas c(device);
1183 // need the unclipped top/left for the translate
1184 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1185 -SkIntToScalar(mask.fBounds.fTop));
1186 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001187
1188 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001189 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001190 SkPaint tmpPaint;
1191 tmpPaint.setFlags(paint.getFlags());
brianosman66a96d02016-05-19 08:43:55 -07001192 tmpPaint.setFilterQuality(paint.getFilterQuality());
Herb Derby4bf560a2017-02-14 11:13:26 -05001193 SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap);
reed@android.com3469c762009-02-24 19:03:20 +00001194 SkRect rr;
1195 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1196 SkIntToScalar(bitmap.height()));
Herb Derby4bf560a2017-02-14 11:13:26 -05001197 c.drawRect(rr, paintWithShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 }
1199 this->drawDevMask(mask, paint);
1200 }
1201}
1202
reed@google.com045e62d2011-10-24 12:19:46 +00001203static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 const SkRect& srcR) {
1205 SkRect dstR;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 m.mapRect(&dstR, srcR);
reedb07a94f2014-11-19 05:03:18 -08001207 return c.quickReject(dstR.roundOut());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208}
1209
reed@google.com045e62d2011-10-24 12:19:46 +00001210static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 int width, int height) {
1212 SkRect r;
1213 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1214 return clipped_out(matrix, clip, r);
1215}
1216
reedc240e712015-05-23 12:26:41 -07001217static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
1218 return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
reed@google.com045e62d2011-10-24 12:19:46 +00001219}
1220
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed03939122014-12-15 13:42:51 -08001222 const SkRect* dstBounds, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001223 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224
1225 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001226 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001228 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 return;
1230 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001231
fmalitaf85d2a42016-10-03 13:46:58 -07001232 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1233 if (origPaint.getStyle() != SkPaint::kFill_Style) {
1234 paint.writable()->setStyle(SkPaint::kFill_Style);
1235 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001236
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 SkMatrix matrix;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001238 matrix.setConcat(*fMatrix, prematrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239
reed@google.com045e62d2011-10-24 12:19:46 +00001240 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 return;
1242 }
1243
fmalitac7e211a2016-01-07 10:34:46 -08001244 if (bitmap.colorType() != kAlpha_8_SkColorType
fmalitaf85d2a42016-10-03 13:46:58 -07001245 && SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001246 //
1247 // It is safe to call lock pixels now, since we know the matrix is
1248 // (more or less) identity.
1249 //
Mike Reed4edb5d22017-04-17 11:02:51 -04001250 SkPixmap pmap;
1251 if (!bitmap.peekPixels(&pmap)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001252 return;
1253 }
reed@google.come1ca7052013-12-17 19:22:07 +00001254 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1255 int iy = SkScalarRoundToInt(matrix.getTranslateY());
reedc240e712015-05-23 12:26:41 -07001256 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
Florin Malita14a64302017-05-24 14:53:44 -04001257 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001258 // blitter will be owned by the allocator.
fmalitaf85d2a42016-10-03 13:46:58 -07001259 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator);
reed@google.com045e62d2011-10-24 12:19:46 +00001260 if (blitter) {
reedc240e712015-05-23 12:26:41 -07001261 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
1262 *fRC, blitter);
reed@google.com045e62d2011-10-24 12:19:46 +00001263 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 }
reedc240e712015-05-23 12:26:41 -07001265 // if !blitter, then we fall-through to the slower case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 // now make a temp draw on the stack, and use it
1270 //
1271 SkDraw draw(*this);
1272 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001273
Matt Sarett70ac8a92016-11-03 10:55:08 -04001274 if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
fmalitaf85d2a42016-10-03 13:46:58 -07001275 draw.drawBitmapAsMask(bitmap, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 } else {
Herb Derby4bf560a2017-02-14 11:13:26 -05001277 SkPaint paintWithShader = make_paint_with_image(*paint, bitmap);
reed03939122014-12-15 13:42:51 -08001278 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1279 if (dstBounds) {
1280 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1281 } else {
1282 draw.drawRect(srcBounds, paintWithShader);
1283 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 }
1285}
1286
reedc240e712015-05-23 12:26:41 -07001287void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001288 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001289
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001291 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001293 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 return;
1295 }
1296
reedc240e712015-05-23 12:26:41 -07001297 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298
reed@google.com045e62d2011-10-24 12:19:46 +00001299 if (fRC->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 return; // nothing to draw
1301 }
1302
reed@google.com40c2ba22011-07-25 19:41:22 +00001303 SkPaint paint(origPaint);
1304 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305
Mike Reed4edb5d22017-04-17 11:02:51 -04001306 SkPixmap pmap;
1307 if (!bitmap.peekPixels(&pmap)) {
reedc240e712015-05-23 12:26:41 -07001308 return;
1309 }
reedc240e712015-05-23 12:26:41 -07001310
halcanary96fcdcc2015-08-27 07:41:13 -07001311 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001312 // blitter will be owned by the allocator.
Florin Malita14a64302017-05-24 14:53:44 -04001313 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
reed41e010c2015-06-09 12:16:53 -07001314 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 if (blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +00001316 SkScan::FillIRect(bounds, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 return;
1318 }
1319 }
1320
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 SkMatrix matrix;
1322 SkRect r;
1323
1324 // get a scalar version of our rect
1325 r.set(bounds);
1326
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001327 // create shader with offset
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 matrix.setTranslate(r.fLeft, r.fTop);
Herb Derby4bf560a2017-02-14 11:13:26 -05001329 SkPaint paintWithShader = make_paint_with_image(paint, bitmap, &matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 SkDraw draw(*this);
1331 matrix.reset();
1332 draw.fMatrix = &matrix;
1333 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001334 // is this OK if paint has a rasterizer?
Herb Derby4bf560a2017-02-14 11:13:26 -05001335 draw.drawRect(r, paintWithShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336}
1337
1338///////////////////////////////////////////////////////////////////////////////
1339
Mike Reed7120b2d2017-05-09 12:16:04 -04001340#include "SkPaintPriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341#include "SkScalerContext.h"
1342#include "SkGlyphCache.h"
reed@google.come6913762012-08-07 15:19:47 +00001343#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344#include "SkUtils.h"
1345
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001346bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
1347 // hairline glyphs are fast enough so we don't need to cache them
1348 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
1349 return true;
1350 }
1351
1352 // we don't cache perspective
1353 if (ctm.hasPerspective()) {
1354 return true;
1355 }
1356
1357 SkMatrix textM;
Mike Reed7120b2d2017-05-09 12:16:04 -04001358 SkPaintPriv::MakeTextMatrix(&textM, paint);
1359 return SkPaint::TooBigToUseCache(ctm, textM);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001360}
1361
Mike Reed99330ba2017-02-22 11:01:08 -05001362void SkDraw::drawText_asPaths(const char text[], size_t byteLength, SkScalar x, SkScalar y,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001364 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365
djsollen@google.com166e6532012-03-20 14:24:38 +00001366 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367
1368 SkMatrix matrix;
1369 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1370 matrix.postTranslate(x, y);
1371
1372 const SkPath* iterPath;
1373 SkScalar xpos, prevXPos = 0;
1374
reed@google.com7b4531f2012-08-07 15:53:00 +00001375 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001377 if (iterPath) {
Mike Reeda1361362017-03-07 09:37:29 -05001378 this->drawPath(*iterPath, iter.getPaint(), &matrix, false);
reed@android.comf2b98d62010-12-20 18:26:13 +00001379 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 prevXPos = xpos;
1381 }
1382}
1383
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384// disable warning : local variable used without having been initialized
bungemand7dc76f2016-03-10 11:14:40 -08001385#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386#pragma warning ( push )
1387#pragma warning ( disable : 4701 )
1388#endif
1389
herbf553e4e2015-11-09 08:51:56 -08001390////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391
herbd4c24f62015-12-07 12:12:29 -08001392class DrawOneGlyph {
1393public:
1394 DrawOneGlyph(const SkDraw& draw, const SkPaint& paint, SkGlyphCache* cache, SkBlitter* blitter)
1395 : fUseRegionToDraw(UsingRegionToDraw(draw.fRC))
1396 , fGlyphCache(cache)
1397 , fBlitter(blitter)
1398 , fClip(fUseRegionToDraw ? &draw.fRC->bwRgn() : nullptr)
1399 , fDraw(draw)
1400 , fPaint(paint)
1401 , fClipBounds(PickClipBounds(draw)) { }
herb11a7f7f2015-11-24 12:41:00 -08001402
herbd4c24f62015-12-07 12:12:29 -08001403 void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
1404 position += rounding;
herbd4c24f62015-12-07 12:12:29 -08001405 // Prevent glyphs from being drawn outside of or straddling the edge of device space.
mtklein875e13c2016-06-19 05:28:33 -07001406 // Comparisons written a little weirdly so that NaN coordinates are treated safely.
1407 auto gt = [](float a, int b) { return !(a <= (float)b); };
1408 auto lt = [](float a, int b) { return !(a >= (float)b); };
1409 if (gt(position.fX, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1410 lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
1411 gt(position.fY, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1412 lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))) {
herbd4c24f62015-12-07 12:12:29 -08001413 return;
1414 }
herb11a7f7f2015-11-24 12:41:00 -08001415
benjaminwagner77a9cc12016-03-25 11:24:30 -07001416 int left = SkScalarFloorToInt(position.fX);
1417 int top = SkScalarFloorToInt(position.fY);
herbd4c24f62015-12-07 12:12:29 -08001418 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1419
1420 left += glyph.fLeft;
1421 top += glyph.fTop;
1422
1423 int right = left + glyph.fWidth;
1424 int bottom = top + glyph.fHeight;
1425
1426 SkMask mask;
1427 mask.fBounds.set(left, top, right, bottom);
mtklein875e13c2016-06-19 05:28:33 -07001428 SkASSERT(!mask.fBounds.isEmpty());
herbd4c24f62015-12-07 12:12:29 -08001429
1430 if (fUseRegionToDraw) {
1431 SkRegion::Cliperator clipper(*fClip, mask.fBounds);
1432
1433 if (!clipper.done() && this->getImageData(glyph, &mask)) {
1434 const SkIRect& cr = clipper.rect();
1435 do {
1436 this->blitMask(mask, cr);
1437 clipper.next();
1438 } while (!clipper.done());
1439 }
1440 } else {
1441 SkIRect storage;
1442 SkIRect* bounds = &mask.fBounds;
1443
1444 // this extra test is worth it, assuming that most of the time it succeeds
1445 // since we can avoid writing to storage
1446 if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) {
1447 if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds))
1448 return;
1449 bounds = &storage;
1450 }
1451
1452 if (this->getImageData(glyph, &mask)) {
1453 this->blitMask(mask, *bounds);
1454 }
1455 }
1456 }
1457
1458private:
1459 static bool UsingRegionToDraw(const SkRasterClip* rClip) {
1460 return rClip->isBW() && !rClip->isRect();
1461 }
1462
1463 static SkIRect PickClipBounds(const SkDraw& draw) {
1464 const SkRasterClip& rasterClip = *draw.fRC;
1465
1466 if (rasterClip.isBW()) {
1467 return rasterClip.bwRgn().getBounds();
1468 } else {
1469 return rasterClip.aaRgn().getBounds();
1470 }
1471 }
1472
1473 bool getImageData(const SkGlyph& glyph, SkMask* mask) {
1474 uint8_t* bits = (uint8_t*)(fGlyphCache->findImage(glyph));
1475 if (nullptr == bits) {
1476 return false; // can't rasterize glyph
1477 }
1478 mask->fImage = bits;
1479 mask->fRowBytes = glyph.rowBytes();
1480 mask->fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1481 return true;
1482 }
1483
herb11a7f7f2015-11-24 12:41:00 -08001484 void blitMask(const SkMask& mask, const SkIRect& clip) const {
1485 if (SkMask::kARGB32_Format == mask.fFormat) {
herbd4c24f62015-12-07 12:12:29 -08001486 SkBitmap bm;
1487 bm.installPixels(
1488 SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()),
1489 (SkPMColor*)mask.fImage, mask.fRowBytes);
1490
1491 fDraw.drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), fPaint);
herb11a7f7f2015-11-24 12:41:00 -08001492 } else {
1493 fBlitter->blitMask(mask, clip);
1494 }
1495 }
1496
herbd4c24f62015-12-07 12:12:29 -08001497 const bool fUseRegionToDraw;
1498 SkGlyphCache * const fGlyphCache;
1499 SkBlitter * const fBlitter;
1500 const SkRegion* const fClip;
1501 const SkDraw& fDraw;
1502 const SkPaint& fPaint;
1503 const SkIRect fClipBounds;
herb11a7f7f2015-11-24 12:41:00 -08001504};
1505
herbd4c24f62015-12-07 12:12:29 -08001506////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507
brianosmana1e8f8d2016-04-08 06:47:54 -07001508uint32_t SkDraw::scalerContextFlags() const {
1509 uint32_t flags = SkPaint::kBoostContrast_ScalerContextFlag;
Mike Reed99330ba2017-02-22 11:01:08 -05001510 if (!fDst.colorSpace()) {
brianosmana1e8f8d2016-04-08 06:47:54 -07001511 flags |= SkPaint::kFakeGamma_ScalerContextFlag;
1512 }
1513 return flags;
bungemanf6d1e602016-02-22 13:20:28 -08001514}
1515
Mike Reed99330ba2017-02-22 11:01:08 -05001516void SkDraw::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y,
1517 const SkPaint& paint, const SkSurfaceProps* props) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001518 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519
reed@android.comf2b98d62010-12-20 18:26:13 +00001520 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521
1522 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001523 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 return;
1525 }
1526
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001527 // SkScalarRec doesn't currently have a way of representing hairline stroke and
1528 // will fill if its frame-width is 0.
reed@google.comed43dff2013-06-04 16:56:27 +00001529 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530 this->drawText_asPaths(text, byteLength, x, y, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531 return;
1532 }
1533
Mike Reed99330ba2017-02-22 11:01:08 -05001534 SkAutoGlyphCache cache(paint, props, this->scalerContextFlags(), fMatrix);
herb11a7f7f2015-11-24 12:41:00 -08001535
1536 // The Blitter Choose needs to be live while using the blitter below.
1537 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1538 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001539 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540
herbe59124e2015-11-18 10:54:39 -08001541 SkFindAndPlaceGlyph::ProcessText(
herb4c11b3f2015-11-20 13:53:12 -08001542 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001543 {x, y}, *fMatrix, paint.getTextAlign(), cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544}
1545
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546//////////////////////////////////////////////////////////////////////////////
1547
Mike Reed99330ba2017-02-22 11:01:08 -05001548void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength, const SkScalar pos[],
1549 int scalarsPerPosition, const SkPoint& offset,
1550 const SkPaint& origPaint, const SkSurfaceProps* props) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001551 // setup our std paint, in hopes of getting hits in the cache
1552 SkPaint paint(origPaint);
1553 SkScalar matrixScale = paint.setupForAsPaths();
1554
reed@google.com5a649022013-06-05 18:00:30 +00001555 SkMatrix matrix;
1556 matrix.setScale(matrixScale, matrixScale);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001557
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001558 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
1559 paint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07001560 paint.setPathEffect(nullptr);
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001561
robertphillipse34f17d2016-07-19 07:59:22 -07001562 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
1563 paint.isDevKernText(),
1564 true);
Mike Reed99330ba2017-02-22 11:01:08 -05001565 SkAutoGlyphCache cache(paint, props, this->scalerContextFlags(), nullptr);
reed@google.comed43dff2013-06-04 16:56:27 +00001566
1567 const char* stop = text + byteLength;
bungeman79738cc2015-03-11 14:05:29 -07001568 SkTextAlignProc alignProc(paint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -07001569 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001570
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001571 // Now restore the original settings, so we "draw" with whatever style/stroking.
1572 paint.setStyle(origPaint.getStyle());
Mike Reed693fdbd2017-01-12 10:13:40 -05001573 paint.setPathEffect(origPaint.refPathEffect());
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001574
reed@google.comed43dff2013-06-04 16:56:27 +00001575 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -08001576 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
reed@google.comed43dff2013-06-04 16:56:27 +00001577 if (glyph.fWidth) {
1578 const SkPath* path = cache->findPath(glyph);
1579 if (path) {
kkinnunencb9a2c82014-06-12 23:06:28 -07001580 SkPoint tmsLoc;
1581 tmsProc(pos, &tmsLoc);
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001582 SkPoint loc;
kkinnunencb9a2c82014-06-12 23:06:28 -07001583 alignProc(tmsLoc, glyph, &loc);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001584
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001585 matrix[SkMatrix::kMTransX] = loc.fX;
1586 matrix[SkMatrix::kMTransY] = loc.fY;
Mike Reed99330ba2017-02-22 11:01:08 -05001587 this->drawPath(*path, paint, &matrix, false);
reed@google.comed43dff2013-06-04 16:56:27 +00001588 }
1589 }
1590 pos += scalarsPerPosition;
1591 }
1592}
1593
Mike Reed99330ba2017-02-22 11:01:08 -05001594void SkDraw::drawPosText(const char text[], size_t byteLength, const SkScalar pos[],
1595 int scalarsPerPosition, const SkPoint& offset, const SkPaint& paint,
1596 const SkSurfaceProps* props) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001597 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1599
reed@android.comf2b98d62010-12-20 18:26:13 +00001600 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601
1602 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001603 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 return;
1605 }
1606
reed@google.comed43dff2013-06-04 16:56:27 +00001607 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
Mike Reed99330ba2017-02-22 11:01:08 -05001608 this->drawPosText_asPaths(text, byteLength, pos, scalarsPerPosition, offset, paint, props);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609 return;
1610 }
1611
Mike Reed99330ba2017-02-22 11:01:08 -05001612 SkAutoGlyphCache cache(paint, props, this->scalerContextFlags(), fMatrix);
herbd4c24f62015-12-07 12:12:29 -08001613
herb9be5ff62015-11-11 11:30:11 -08001614 // The Blitter Choose needs to be live while using the blitter below.
herb11a7f7f2015-11-24 12:41:00 -08001615 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1616 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001617 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
herbd4c24f62015-12-07 12:12:29 -08001618 SkPaint::Align textAlignment = paint.getTextAlign();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619
herb9be5ff62015-11-11 11:30:11 -08001620 SkFindAndPlaceGlyph::ProcessPosText(
herb4c11b3f2015-11-20 13:53:12 -08001621 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001622 offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623}
1624
bungemand7dc76f2016-03-10 11:14:40 -08001625#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626#pragma warning ( pop )
1627#endif
1628
Mike Reed787a16d2017-05-15 09:29:18 -04001629////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630
1631#ifdef SK_DEBUG
1632
reed@android.comf2b98d62010-12-20 18:26:13 +00001633void SkDraw::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -07001634 SkASSERT(fMatrix != nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -07001635 SkASSERT(fRC != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636
reed@google.com045e62d2011-10-24 12:19:46 +00001637 const SkIRect& cr = fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638 SkIRect br;
1639
reed41e010c2015-06-09 12:16:53 -07001640 br.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 SkASSERT(cr.isEmpty() || br.contains(cr));
1642}
1643
1644#endif
1645
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646////////////////////////////////////////////////////////////////////////////////////////////////
1647
1648#include "SkPath.h"
1649#include "SkDraw.h"
1650#include "SkRegion.h"
1651#include "SkBlitter.h"
1652
1653static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
reedb07a94f2014-11-19 05:03:18 -08001654 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 SkIRect* bounds) {
1656 if (devPath.isEmpty()) {
1657 return false;
1658 }
1659
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660 // init our bounds from the path
reed11fa2242015-03-13 06:08:28 -07001661 *bounds = devPath.getBounds().makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
reed@google.coma76de3d2011-01-13 18:30:42 +00001662
tomhudson@google.com6db75fc2012-03-23 14:46:48 +00001663 SkIPoint margin = SkIPoint::Make(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664 if (filter) {
1665 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00001666
bungeman@google.com5af16f82011-09-02 15:06:44 +00001667 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00001668
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669 srcM.fBounds = *bounds;
1670 srcM.fFormat = SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
1672 return false;
1673 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 }
1675
bungeman@google.com5af16f82011-09-02 15:06:44 +00001676 // (possibly) trim the bounds to reflect the clip
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677 // (plus whatever slop the filter needs)
bungeman@google.com5af16f82011-09-02 15:06:44 +00001678 if (clipBounds) {
reed@android.com35555912009-03-16 18:46:55 +00001679 // Ugh. Guard against gigantic margins from wacky filters. Without this
1680 // check we can request arbitrary amounts of slop beyond our visible
1681 // clip, and bring down the renderer (at least on finite RAM machines
1682 // like handsets, etc.). Need to balance this invented value between
1683 // quality of large filters like blurs, and the corresponding memory
1684 // requests.
1685 static const int MAX_MARGIN = 128;
reed11fa2242015-03-13 06:08:28 -07001686 if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
1687 SkMin32(margin.fY, MAX_MARGIN)))) {
bungeman@google.com5af16f82011-09-02 15:06:44 +00001688 return false;
1689 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001690 }
1691
1692 return true;
1693}
1694
bsalomon055e1922016-05-06 07:22:58 -07001695static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
1696 SkStrokeRec::InitStyle style) {
reed41e010c2015-06-09 12:16:53 -07001697 SkDraw draw;
1698 if (!draw.fDst.reset(mask)) {
1699 return;
1700 }
1701
reed@google.com045e62d2011-10-24 12:19:46 +00001702 SkRasterClip clip;
1703 SkMatrix matrix;
1704 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705
reed@google.com045e62d2011-10-24 12:19:46 +00001706 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
1708 -SkIntToScalar(mask.fBounds.fTop));
1709
reed@google.com045e62d2011-10-24 12:19:46 +00001710 draw.fRC = &clip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 draw.fMatrix = &matrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712 paint.setAntiAlias(true);
bsalomon055e1922016-05-06 07:22:58 -07001713 switch (style) {
1714 case SkStrokeRec::kHairline_InitStyle:
1715 SkASSERT(!paint.getStrokeWidth());
1716 paint.setStyle(SkPaint::kStroke_Style);
1717 break;
1718 case SkStrokeRec::kFill_InitStyle:
1719 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
1720 break;
1721
1722 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 draw.drawPath(devPath, paint);
1724}
1725
1726bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +00001727 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001728 SkMask* mask, SkMask::CreateMode mode,
bsalomon055e1922016-05-06 07:22:58 -07001729 SkStrokeRec::InitStyle style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 if (SkMask::kJustRenderImage_CreateMode != mode) {
1731 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
1732 return false;
1733 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001734
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
1736 mask->fFormat = SkMask::kA8_Format;
1737 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00001738 size_t size = mask->computeImageSize();
1739 if (0 == size) {
1740 // we're too big to allocate the mask, abort
1741 return false;
1742 }
1743 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 memset(mask->fImage, 0, mask->computeImageSize());
1745 }
1746
1747 if (SkMask::kJustComputeBounds_CreateMode != mode) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001748 draw_into_mask(*mask, devPath, style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001750
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 return true;
1752}