blob: 03ca87eb9fb80b1e1c0a83b15977acb05d9d22d3 [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 */
bungeman903dcb02015-03-16 13:00:09 -04007#define __STDC_LIMIT_MACROS
epoger@google.comec3ed6a2011-07-28 14:26:00 +00008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkDraw.h"
Herb Derby83e939b2017-02-07 14:25:11 -050010
11#include "SkArenaAlloc.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"
15#include "SkColorPriv.h"
Florin Malita4b665122017-02-14 16:19:49 -050016#include "SkColorShader.h"
Mike Reed986480a2017-01-13 22:43:16 +000017#include "SkDevice.h"
reed@google.com1c028bd2013-08-28 15:23:19 +000018#include "SkDeviceLooper.h"
herbe5911c92015-11-09 13:15:21 -080019#include "SkFindAndPlaceGlyph.h"
bungeman@google.com2211b622012-01-13 15:02:58 +000020#include "SkFixed.h"
Florin Malita00dca8c2017-01-27 12:05:27 -050021#include "SkLocalMatrixShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkMaskFilter.h"
herbf553e4e2015-11-09 08:51:56 -080023#include "SkMatrix.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"
Florin Malita4b665122017-02-14 16:19:49 -050037#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000038#include "SkUtils.h"
commit-bot@chromium.org559a8832014-05-30 10:08:22 +000039#include "SkVertState.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
reed@android.com8a1c16f2008-12-17 15:59:43 +000041#include "SkBitmapProcShader.h"
42#include "SkDrawProcs.h"
reed@google.comae573582013-01-03 15:22:40 +000043#include "SkMatrixUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000044
45//#define TRACE_BITMAP_DRAWS
46
herbd4c24f62015-12-07 12:12:29 -080047// Helper function to fix code gen bug on ARM64.
48// See SkFindAndPlaceGlyph.h for more details.
49void FixGCC49Arm64Bug(int v) { }
reed@android.com8a1c16f2008-12-17 15:59:43 +000050
reed@google.comfd4236e2011-07-25 21:16:22 +000051/** Helper for allocating small blitters on the stack.
52 */
reed@google.com40c2ba22011-07-25 19:41:22 +000053class SkAutoBlitterChoose : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000054public:
reed@google.com1d6ee0b2011-07-05 17:44:56 +000055 SkAutoBlitterChoose() {
halcanary96fcdcc2015-08-27 07:41:13 -070056 fBlitter = nullptr;
reed@google.com1d6ee0b2011-07-05 17:44:56 +000057 }
reed41e010c2015-06-09 12:16:53 -070058 SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix,
reed@google.com126f7f52013-11-07 16:06:53 +000059 const SkPaint& paint, bool drawCoverage = false) {
Herb Derby83e939b2017-02-07 14:25:11 -050060 fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000061 }
herbe59124e2015-11-18 10:54:39 -080062
reed@android.com8a1c16f2008-12-17 15:59:43 +000063 SkBlitter* operator->() { return fBlitter; }
64 SkBlitter* get() const { return fBlitter; }
65
reed41e010c2015-06-09 12:16:53 -070066 void choose(const SkPixmap& dst, const SkMatrix& matrix,
krajcevski53f09592014-08-06 11:12:14 -070067 const SkPaint& paint, bool drawCoverage = false) {
reed@google.com1d6ee0b2011-07-05 17:44:56 +000068 SkASSERT(!fBlitter);
Herb Derby83e939b2017-02-07 14:25:11 -050069 fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
reed@google.com1d6ee0b2011-07-05 17:44:56 +000070 }
71
reed@android.com8a1c16f2008-12-17 15:59:43 +000072private:
Herb Derby34e91ca2017-02-10 16:57:57 -050073 // Owned by fAlloc, which will handle the delete.
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000074 SkBlitter* fBlitter;
Herb Derby83e939b2017-02-07 14:25:11 -050075
Mike Reedc4640352017-03-02 13:39:25 -050076 char fStorage[kSkBlitterContextSize];
77 SkArenaAlloc fAlloc{fStorage};
reed@android.com8a1c16f2008-12-17 15:59:43 +000078};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000079#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose)
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
Herb Derby4bf560a2017-02-14 11:13:26 -050081static SkPaint make_paint_with_image(
82 const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) {
83 SkPaint paint(origPaint);
84 paint.setShader(SkMakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
85 SkShader::kClamp_TileMode, matrix,
86 kNever_SkCopyPixelsMode));
87 return paint;
88}
reed@android.com8a1c16f2008-12-17 15:59:43 +000089
reed@android.com8a1c16f2008-12-17 15:59:43 +000090///////////////////////////////////////////////////////////////////////////////
91
reed@android.comf2b98d62010-12-20 18:26:13 +000092SkDraw::SkDraw() {
93 sk_bzero(this, sizeof(*this));
94}
95
reed@google.com4bbdeac2013-01-24 21:03:11 +000096bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
97 if (fRC->isEmpty()) {
98 return false;
99 }
100
101 SkMatrix inverse;
102 if (!fMatrix->invert(&inverse)) {
103 return false;
104 }
105
106 SkIRect devBounds = fRC->getBounds();
107 // outset to have slop for antialasing and hairlines
108 devBounds.outset(1, 1);
109 inverse.mapRect(localBounds, SkRect::Make(devBounds));
110 return true;
111}
112
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113///////////////////////////////////////////////////////////////////////////////
114
115typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
116
117static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +0000118 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119}
120
121static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
122
123static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000124 sk_memset32((uint32_t*)pixels, data, SkToInt(bytes >> 2));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125}
126
127static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000128 sk_memset16((uint16_t*)pixels, data, SkToInt(bytes >> 1));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129}
130
131static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
132 memset(pixels, data, bytes);
133}
134
reed41e010c2015-06-09 12:16:53 -0700135static BitmapXferProc ChooseBitmapXferProc(const SkPixmap& dst, const SkPaint& paint,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 uint32_t* data) {
137 // todo: we can apply colorfilter up front if no shader, so we wouldn't
138 // need to abort this fastpath
139 if (paint.getShader() || paint.getColorFilter()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700140 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 }
142
reed374772b2016-10-05 17:33:02 -0700143 SkBlendMode mode = paint.getBlendMode();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000145
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 // collaps modes based on color...
reed374772b2016-10-05 17:33:02 -0700147 if (SkBlendMode::kSrcOver == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 unsigned alpha = SkColorGetA(color);
149 if (0 == alpha) {
reed374772b2016-10-05 17:33:02 -0700150 mode = SkBlendMode::kDst;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 } else if (0xFF == alpha) {
reed374772b2016-10-05 17:33:02 -0700152 mode = SkBlendMode::kSrc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 }
154 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000155
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 switch (mode) {
reed374772b2016-10-05 17:33:02 -0700157 case SkBlendMode::kClear:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158// SkDebugf("--- D_Clear_BitmapXferProc\n");
159 return D_Clear_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700160 case SkBlendMode::kDst:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161// SkDebugf("--- D_Dst_BitmapXferProc\n");
162 return D_Dst_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700163 case SkBlendMode::kSrc: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000165 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 */
167 SkPMColor pmc = SkPreMultiplyColor(color);
reed41e010c2015-06-09 12:16:53 -0700168 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000169 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 if (data) {
171 *data = pmc;
172 }
173// SkDebugf("--- D32_Src_BitmapXferProc\n");
174 return D32_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000175 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 if (data) {
177 *data = SkPixel32ToPixel16(pmc);
178 }
179// SkDebugf("--- D16_Src_BitmapXferProc\n");
180 return D16_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000181 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 if (data) {
183 *data = SkGetPackedA32(pmc);
184 }
185// SkDebugf("--- DA8_Src_BitmapXferProc\n");
186 return DA8_Src_BitmapXferProc;
187 default:
188 break;
189 }
190 break;
191 }
192 default:
193 break;
194 }
halcanary96fcdcc2015-08-27 07:41:13 -0700195 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196}
197
reed41e010c2015-06-09 12:16:53 -0700198static void CallBitmapXferProc(const SkPixmap& dst, const SkIRect& rect, BitmapXferProc proc,
199 uint32_t procData) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 int shiftPerPixel;
reed41e010c2015-06-09 12:16:53 -0700201 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000202 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 shiftPerPixel = 2;
204 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000205 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 shiftPerPixel = 1;
207 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000208 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 shiftPerPixel = 0;
210 break;
211 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000212 SkDEBUGFAIL("Can't use xferproc on this config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 return;
214 }
215
reed41e010c2015-06-09 12:16:53 -0700216 uint8_t* pixels = (uint8_t*)dst.writable_addr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 SkASSERT(pixels);
reed41e010c2015-06-09 12:16:53 -0700218 const size_t rowBytes = dst.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 const int widthBytes = rect.width() << shiftPerPixel;
220
221 // skip down to the first scanline and X position
222 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
223 for (int scans = rect.height() - 1; scans >= 0; --scans) {
224 proc(pixels, widthBytes, procData);
225 pixels += rowBytes;
226 }
227}
228
229void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000230 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231
reed@google.com045e62d2011-10-24 12:19:46 +0000232 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 return;
234 }
235
236 SkIRect devRect;
reed41e010c2015-06-09 12:16:53 -0700237 devRect.set(0, 0, fDst.width(), fDst.height());
reed@google.coma76de3d2011-01-13 18:30:42 +0000238
reed@google.com045e62d2011-10-24 12:19:46 +0000239 if (fRC->isBW()) {
240 /* If we don't have a shader (i.e. we're just a solid color) we may
241 be faster to operate directly on the device bitmap, rather than invoking
242 a blitter. Esp. true for xfermodes, which require a colorshader to be
243 present, which is just redundant work. Since we're drawing everywhere
244 in the clip, we don't have to worry about antialiasing.
245 */
246 uint32_t procData = 0; // to avoid the warning
reed41e010c2015-06-09 12:16:53 -0700247 BitmapXferProc proc = ChooseBitmapXferProc(fDst, paint, &procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000248 if (proc) {
249 if (D_Dst_BitmapXferProc == proc) { // nothing to do
250 return;
251 }
252
253 SkRegion::Iterator iter(fRC->bwRgn());
254 while (!iter.done()) {
reed41e010c2015-06-09 12:16:53 -0700255 CallBitmapXferProc(fDst, iter.rect(), proc, procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000256 iter.next();
257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 return;
259 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 }
reed@google.com045e62d2011-10-24 12:19:46 +0000261
262 // normal case: use a blitter
reed41e010c2015-06-09 12:16:53 -0700263 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000264 SkScan::FillIRect(devRect, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265}
266
267///////////////////////////////////////////////////////////////////////////////
268
269struct PtProcRec {
270 SkCanvas::PointMode fMode;
271 const SkPaint* fPaint;
272 const SkRegion* fClip;
reed@google.com045e62d2011-10-24 12:19:46 +0000273 const SkRasterClip* fRC;
reed@google.coma76de3d2011-01-13 18:30:42 +0000274
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 // computed values
276 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000277
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
279 SkBlitter*);
280
281 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000282 const SkRasterClip*);
283 Proc chooseProc(SkBlitter** blitter);
284
285private:
286 SkAAClipBlitterWrapper fWrapper;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287};
288
289static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
290 int count, SkBlitter* blitter) {
291 SkASSERT(rec.fClip->isRect());
292 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000293
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000295 int x = SkScalarFloorToInt(devPts[i].fX);
296 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 if (r.contains(x, y)) {
298 blitter->blitH(x, y, 1);
299 }
300 }
301}
302
303static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
304 const SkPoint devPts[], int count,
305 SkBlitter* blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +0000306 SkASSERT(rec.fRC->isRect());
307 const SkIRect& r = rec.fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700309 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
310 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000311
reed41e010c2015-06-09 12:16:53 -0700312 uint16_t* addr = dst->writable_addr16(0, 0);
313 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000314
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000316 int x = SkScalarFloorToInt(devPts[i].fX);
317 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 if (r.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
320 }
321 }
322}
323
reed@google.com2d47a212012-11-29 21:01:00 +0000324static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
325 const SkPoint devPts[], int count,
326 SkBlitter* blitter) {
327 SkASSERT(rec.fRC->isRect());
328 const SkIRect& r = rec.fRC->getBounds();
329 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700330 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
331 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000332
reed41e010c2015-06-09 12:16:53 -0700333 SkPMColor* addr = dst->writable_addr32(0, 0);
334 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000335
reed@google.com2d47a212012-11-29 21:01:00 +0000336 for (int i = 0; i < count; i++) {
337 int x = SkScalarFloorToInt(devPts[i].fX);
338 int y = SkScalarFloorToInt(devPts[i].fY);
339 if (r.contains(x, y)) {
340 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
341 }
342 }
343}
344
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
346 int count, SkBlitter* blitter) {
347 for (int i = 0; i < count; i++) {
reed@google.come1ca7052013-12-17 19:22:07 +0000348 int x = SkScalarFloorToInt(devPts[i].fX);
349 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 if (rec.fClip->contains(x, y)) {
351 blitter->blitH(x, y, 1);
352 }
353 }
354}
355
356static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
357 int count, SkBlitter* blitter) {
358 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700359 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 }
361}
362
363static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
364 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700365 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366}
367
368// aa versions
369
370static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
371 int count, SkBlitter* blitter) {
372 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700373 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 }
375}
376
377static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
378 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700379 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380}
381
382// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
383
384static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
385 int count, SkBlitter* blitter) {
386 const SkFixed radius = rec.fRadius;
387 for (int i = 0; i < count; i++) {
388 SkFixed x = SkScalarToFixed(devPts[i].fX);
389 SkFixed y = SkScalarToFixed(devPts[i].fY);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 SkXRect r;
392 r.fLeft = x - radius;
393 r.fTop = y - radius;
394 r.fRight = x + radius;
395 r.fBottom = y + radius;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000396
reed@google.com045e62d2011-10-24 12:19:46 +0000397 SkScan::FillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398 }
399}
400
401static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
402 int count, SkBlitter* blitter) {
403 const SkFixed radius = rec.fRadius;
404 for (int i = 0; i < count; i++) {
405 SkFixed x = SkScalarToFixed(devPts[i].fX);
406 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000407
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 SkXRect r;
409 r.fLeft = x - radius;
410 r.fTop = y - radius;
411 r.fRight = x + radius;
412 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000413
reed@google.com045e62d2011-10-24 12:19:46 +0000414 SkScan::AntiFillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 }
416}
417
reed@android.comb4f404a2009-07-10 17:02:17 +0000418// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
reed@google.com045e62d2011-10-24 12:19:46 +0000420 const SkMatrix* matrix, const SkRasterClip* rc) {
ochang3ece53e2015-05-21 15:44:53 -0700421 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
422 return false;
423 }
424
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 if (paint.getPathEffect()) {
426 return false;
427 }
428 SkScalar width = paint.getStrokeWidth();
429 if (0 == width) {
430 fMode = mode;
431 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700432 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000433 fRC = rc;
reed@google.com2d47a212012-11-29 21:01:00 +0000434 fRadius = SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 return true;
436 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000437 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
robertphillips9f2251c2014-11-04 13:33:50 -0800438 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
440 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
441 if (SkScalarNearlyZero(sx - sy)) {
442 if (sx < 0) {
443 sx = -sx;
444 }
445
446 fMode = mode;
447 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700448 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000449 fRC = rc;
Mike Reeda99b6ce2017-02-04 11:04:26 -0500450 fRadius = SkScalarToFixed(width * sx) >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 return true;
452 }
453 }
454 return false;
455}
456
reed@google.com045e62d2011-10-24 12:19:46 +0000457PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700458 Proc proc = nullptr;
reed@google.coma76de3d2011-01-13 18:30:42 +0000459
reed@google.com045e62d2011-10-24 12:19:46 +0000460 SkBlitter* blitter = *blitterPtr;
461 if (fRC->isBW()) {
462 fClip = &fRC->bwRgn();
463 } else {
464 fWrapper.init(*fRC, blitter);
465 fClip = &fWrapper.getRgn();
466 blitter = fWrapper.getBlitter();
467 *blitterPtr = blitter;
468 }
469
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470 // for our arrays
471 SkASSERT(0 == SkCanvas::kPoints_PointMode);
472 SkASSERT(1 == SkCanvas::kLines_PointMode);
473 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
474 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
475
reed@google.com2d47a212012-11-29 21:01:00 +0000476 if (fPaint->isAntiAlias()) {
477 if (0 == fPaint->getStrokeWidth()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 static const Proc gAAProcs[] = {
479 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
480 };
481 proc = gAAProcs[fMode];
reed@google.com2d47a212012-11-29 21:01:00 +0000482 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
483 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
484 proc = aa_square_proc;
485 }
486 } else { // BW
487 if (fRadius <= SK_FixedHalf) { // small radii and hairline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
489 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700490 const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
reed@google.com900ecf22014-02-20 20:55:37 +0000491 if (bm && kRGB_565_SkColorType == bm->colorType()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 proc = bw_pt_rect_16_hair_proc;
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000493 } else if (bm && kN32_SkColorType == bm->colorType()) {
reed@google.com2d47a212012-11-29 21:01:00 +0000494 proc = bw_pt_rect_32_hair_proc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 } else {
496 proc = bw_pt_rect_hair_proc;
497 }
498 } else {
499 static Proc gBWProcs[] = {
500 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
501 };
502 proc = gBWProcs[fMode];
503 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 } else {
505 proc = bw_square_proc;
506 }
507 }
508 return proc;
509}
510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511// each of these costs 8-bytes of stack space, so don't make it too large
512// must be even for lines/polygon to work
513#define MAX_DEV_PTS 32
514
515void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000516 const SkPoint pts[], const SkPaint& paint,
Mike Reed99330ba2017-02-22 11:01:08 -0500517 SkBaseDevice* device) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 // if we're in lines mode, force count to be even
519 if (SkCanvas::kLines_PointMode == mode) {
520 count &= ~(size_t)1;
521 }
522
523 if ((long)count <= 0) {
524 return;
525 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000526
halcanary96fcdcc2015-08-27 07:41:13 -0700527 SkASSERT(pts != nullptr);
reed@android.comf2b98d62010-12-20 18:26:13 +0000528 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000529
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000531 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 return;
533 }
534
535 PtProcRec rec;
Mike Reed99330ba2017-02-22 11:01:08 -0500536 if (!device && rec.init(mode, paint, fMatrix, fRC)) {
reed41e010c2015-06-09 12:16:53 -0700537 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538
539 SkPoint devPts[MAX_DEV_PTS];
540 const SkMatrix* matrix = fMatrix;
541 SkBlitter* bltr = blitter.get();
reed@google.com045e62d2011-10-24 12:19:46 +0000542 PtProcRec::Proc proc = rec.chooseProc(&bltr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 // we have to back up subsequent passes if we're in polygon mode
544 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000545
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 do {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000547 int n = SkToInt(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 if (n > MAX_DEV_PTS) {
549 n = MAX_DEV_PTS;
550 }
551 matrix->mapPoints(devPts, pts, n);
552 proc(rec, devPts, n, bltr);
553 pts += n - backup;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000554 SkASSERT(SkToInt(count) >= n);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 count -= n;
556 if (count > 0) {
557 count += backup;
558 }
559 } while (count != 0);
560 } else {
561 switch (mode) {
562 case SkCanvas::kPoints_PointMode: {
563 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000564 SkPaint newPaint(paint);
565 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566
reed@google.com40c2ba22011-07-25 19:41:22 +0000567 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000569
reed@google.com40c2ba22011-07-25 19:41:22 +0000570 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 SkPath path;
572 SkMatrix preMatrix;
mtklein7ef849d2014-11-24 09:11:45 -0800573
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 path.addCircle(0, 0, radius);
575 for (size_t i = 0; i < count; i++) {
576 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
577 // pass true for the last point, since we can modify
578 // then path then
jvanverthb3eb6872014-10-24 07:12:51 -0700579 path.setIsVolatile((count-1) == i);
Mike Reed99330ba2017-02-22 11:01:08 -0500580 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500581 device->drawPath(path, newPaint, &preMatrix, (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000582 } else {
Mike Reeda1361362017-03-07 09:37:29 -0500583 this->drawPath(path, newPaint, &preMatrix, (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 }
586 } else {
587 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000588
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 for (size_t i = 0; i < count; i++) {
590 r.fLeft = pts[i].fX - radius;
591 r.fTop = pts[i].fY - radius;
592 r.fRight = r.fLeft + width;
593 r.fBottom = r.fTop + width;
Mike Reed99330ba2017-02-22 11:01:08 -0500594 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500595 device->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000596 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000597 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000598 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 }
600 }
601 break;
602 }
603 case SkCanvas::kLines_PointMode:
bsalomon49f085d2014-09-05 13:34:00 -0700604 if (2 == count && paint.getPathEffect()) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000605 // most likely a dashed line - see if it is one of the ones
606 // we can accelerate
607 SkStrokeRec rec(paint);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000608 SkPathEffect::PointData pointData;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000609
610 SkPath path;
611 path.moveTo(pts[0]);
612 path.lineTo(pts[1]);
613
reed@google.com4bbdeac2013-01-24 21:03:11 +0000614 SkRect cullRect = SkRect::Make(fRC->getBounds());
skia.committer@gmail.com4024f322013-01-25 07:06:46 +0000615
reed@google.com4bbdeac2013-01-24 21:03:11 +0000616 if (paint.getPathEffect()->asPoints(&pointData, path, rec,
617 *fMatrix, &cullRect)) {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000618 // 'asPoints' managed to find some fast path
619
robertphillips@google.com629ab542012-11-28 17:18:11 +0000620 SkPaint newP(paint);
halcanary96fcdcc2015-08-27 07:41:13 -0700621 newP.setPathEffect(nullptr);
robertphillips@google.com935ad022012-12-05 19:07:21 +0000622 newP.setStyle(SkPaint::kFill_Style);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000623
robertphillips@google.com6d875572012-12-17 18:56:29 +0000624 if (!pointData.fFirst.isEmpty()) {
Mike Reed99330ba2017-02-22 11:01:08 -0500625 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500626 device->drawPath(pointData.fFirst, newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000627 } else {
628 this->drawPath(pointData.fFirst, newP);
629 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000630 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000631
632 if (!pointData.fLast.isEmpty()) {
Mike Reed99330ba2017-02-22 11:01:08 -0500633 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500634 device->drawPath(pointData.fLast, newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000635 } else {
636 this->drawPath(pointData.fLast, newP);
637 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000638 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000639
640 if (pointData.fSize.fX == pointData.fSize.fY) {
641 // The rest of the dashed line can just be drawn as points
642 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
643
644 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
645 newP.setStrokeCap(SkPaint::kRound_Cap);
646 } else {
647 newP.setStrokeCap(SkPaint::kButt_Cap);
648 }
649
Mike Reed99330ba2017-02-22 11:01:08 -0500650 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500651 device->drawPoints(SkCanvas::kPoints_PointMode,
652 pointData.fNumPoints,
653 pointData.fPoints,
654 newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000655 } else {
656 this->drawPoints(SkCanvas::kPoints_PointMode,
657 pointData.fNumPoints,
658 pointData.fPoints,
659 newP,
Mike Reed99330ba2017-02-22 11:01:08 -0500660 device);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000661 }
662 break;
663 } else {
664 // The rest of the dashed line must be drawn as rects
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000665 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
robertphillips@google.com6d875572012-12-17 18:56:29 +0000666 pointData.fFlags));
667
668 SkRect r;
669
670 for (int i = 0; i < pointData.fNumPoints; ++i) {
671 r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
672 pointData.fPoints[i].fY - pointData.fSize.fY,
673 pointData.fPoints[i].fX + pointData.fSize.fX,
674 pointData.fPoints[i].fY + pointData.fSize.fY);
Mike Reed99330ba2017-02-22 11:01:08 -0500675 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500676 device->drawRect(r, newP);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000677 } else {
678 this->drawRect(r, newP);
679 }
680 }
681 }
682
robertphillips@google.com629ab542012-11-28 17:18:11 +0000683 break;
684 }
685 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000686 // couldn't take fast path so fall through!
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 case SkCanvas::kPolygon_PointMode: {
688 count -= 1;
689 SkPath path;
690 SkPaint p(paint);
691 p.setStyle(SkPaint::kStroke_Style);
692 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
jvanverthb3eb6872014-10-24 07:12:51 -0700693 path.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 for (size_t i = 0; i < count; i += inc) {
695 path.moveTo(pts[i]);
696 path.lineTo(pts[i+1]);
Mike Reed99330ba2017-02-22 11:01:08 -0500697 if (device) {
Mike Reeda1361362017-03-07 09:37:29 -0500698 device->drawPath(path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000699 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700700 this->drawPath(path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000701 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 path.rewind();
703 }
704 break;
705 }
706 }
707 }
708}
709
fmalita1a178ca2015-01-15 06:01:23 -0800710static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
711 SkASSERT(matrix.rectStaysRect());
712 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
713
714 SkVector size;
715 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
716 matrix.mapVectors(&size, &pt, 1);
717 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
718}
719
reed@google.com761fb622011-04-04 18:58:05 +0000720static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
721 SkPoint* strokeSize) {
722 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
723 paint.getStrokeMiter() < SK_ScalarSqrt2) {
724 return false;
725 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000726
fmalita1a178ca2015-01-15 06:01:23 -0800727 *strokeSize = compute_stroke_size(paint, matrix);
reed@google.com761fb622011-04-04 18:58:05 +0000728 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000729}
730
reed@google.com62ab7ad2011-04-05 14:08:25 +0000731SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
732 const SkMatrix& matrix,
733 SkPoint* strokeSize) {
734 RectType rtype;
735 const SkScalar width = paint.getStrokeWidth();
736 const bool zeroWidth = (0 == width);
737 SkPaint::Style style = paint.getStyle();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000738
reed@google.com62ab7ad2011-04-05 14:08:25 +0000739 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
740 style = SkPaint::kFill_Style;
741 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000742
reed@google.com62ab7ad2011-04-05 14:08:25 +0000743 if (paint.getPathEffect() || paint.getMaskFilter() ||
744 paint.getRasterizer() || !matrix.rectStaysRect() ||
745 SkPaint::kStrokeAndFill_Style == style) {
746 rtype = kPath_RectType;
747 } else if (SkPaint::kFill_Style == style) {
748 rtype = kFill_RectType;
749 } else if (zeroWidth) {
750 rtype = kHair_RectType;
751 } else if (easy_rect_join(paint, matrix, strokeSize)) {
752 rtype = kStroke_RectType;
753 } else {
754 rtype = kPath_RectType;
755 }
756 return rtype;
757}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000758
reed@google.com73244152012-05-11 14:35:23 +0000759static const SkPoint* rect_points(const SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000760 return SkTCast<const SkPoint*>(&r);
reed@google.com73244152012-05-11 14:35:23 +0000761}
762
763static SkPoint* rect_points(SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000764 return SkTCast<SkPoint*>(&r);
reed@google.com40c2ba22011-07-25 19:41:22 +0000765}
766
reed03939122014-12-15 13:42:51 -0800767void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
768 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000769 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770
771 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000772 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 return;
774 }
775
reed03939122014-12-15 13:42:51 -0800776 const SkMatrix* matrix;
777 SkMatrix combinedMatrixStorage;
778 if (paintMatrix) {
779 SkASSERT(postPaintRect);
780 combinedMatrixStorage.setConcat(*fMatrix, *paintMatrix);
781 matrix = &combinedMatrixStorage;
782 } else {
783 SkASSERT(!postPaintRect);
784 matrix = fMatrix;
785 }
786
reed@google.com761fb622011-04-04 18:58:05 +0000787 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000788 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000789
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000790 if (kPath_RectType == rtype) {
reed03939122014-12-15 13:42:51 -0800791 SkDraw draw(*this);
792 if (paintMatrix) {
793 draw.fMatrix = matrix;
794 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 SkPath tmp;
reed03939122014-12-15 13:42:51 -0800796 tmp.addRect(prePaintRect);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 tmp.setFillType(SkPath::kWinding_FillType);
halcanary96fcdcc2015-08-27 07:41:13 -0700798 draw.drawPath(tmp, paint, nullptr, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 return;
800 }
801
reed03939122014-12-15 13:42:51 -0800802 SkRect devRect;
fmalita1a178ca2015-01-15 06:01:23 -0800803 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
804 // skip the paintMatrix when transforming the rect by the CTM
805 fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
reed@google.com73244152012-05-11 14:35:23 +0000806 devRect.sort();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 // look for the quick exit, before we build a blitter
fmalita1a178ca2015-01-15 06:01:23 -0800809 SkRect bbox = devRect;
reed@google.com1c028bd2013-08-28 15:23:19 +0000810 if (paint.getStyle() != SkPaint::kFill_Style) {
811 // extra space for hairlines
georgeb3eba472014-09-09 11:33:57 -0700812 if (paint.getStrokeWidth() == 0) {
fmalita1a178ca2015-01-15 06:01:23 -0800813 bbox.outset(1, 1);
georgeb3eba472014-09-09 11:33:57 -0700814 } else {
fmalita1a178ca2015-01-15 06:01:23 -0800815 // For kStroke_RectType, strokeSize is already computed.
816 const SkPoint& ssize = (kStroke_RectType == rtype)
817 ? strokeSize
818 : compute_stroke_size(paint, *fMatrix);
819 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
georgeb3eba472014-09-09 11:33:57 -0700820 }
reed@google.com1c028bd2013-08-28 15:23:19 +0000821 }
fmalita1a178ca2015-01-15 06:01:23 -0800822
823 SkIRect ir = bbox.roundOut();
reed@google.com1c028bd2013-08-28 15:23:19 +0000824 if (fRC->quickReject(ir)) {
825 return;
rmistry@google.come09d6f42013-08-27 18:53:41 +0000826 }
827
reed41e010c2015-06-09 12:16:53 -0700828 SkDeviceLooper looper(fDst, *fRC, ir, paint.isAntiAlias());
reed@google.com1c028bd2013-08-28 15:23:19 +0000829 while (looper.next()) {
830 SkRect localDevRect;
831 looper.mapRect(&localDevRect, devRect);
832 SkMatrix localMatrix;
reed03939122014-12-15 13:42:51 -0800833 looper.mapMatrix(&localMatrix, *matrix);
rmistry@google.come09d6f42013-08-27 18:53:41 +0000834
reed41e010c2015-06-09 12:16:53 -0700835 SkAutoBlitterChoose blitterStorage(looper.getPixmap(), localMatrix, paint);
reed@google.com1c028bd2013-08-28 15:23:19 +0000836 const SkRasterClip& clip = looper.getRC();
837 SkBlitter* blitter = blitterStorage.get();
838
839 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
840 // case we are also hairline (if we've gotten to here), which devolves to
841 // effectively just kFill
842 switch (rtype) {
843 case kFill_RectType:
844 if (paint.isAntiAlias()) {
845 SkScan::AntiFillRect(localDevRect, clip, blitter);
846 } else {
847 SkScan::FillRect(localDevRect, clip, blitter);
848 }
849 break;
850 case kStroke_RectType:
851 if (paint.isAntiAlias()) {
852 SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
853 } else {
854 SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
855 }
856 break;
857 case kHair_RectType:
858 if (paint.isAntiAlias()) {
859 SkScan::AntiHairRect(localDevRect, clip, blitter);
860 } else {
861 SkScan::HairRect(localDevRect, clip, blitter);
862 }
863 break;
864 default:
865 SkDEBUGFAIL("bad rtype");
866 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 }
868}
869
870void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
871 if (srcM.fBounds.isEmpty()) {
872 return;
873 }
874
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000875 const SkMask* mask = &srcM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000877 SkMask dstM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 if (paint.getMaskFilter() &&
robertphillipse80eb922015-12-17 11:33:12 -0800879 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 mask = &dstM;
881 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000882 SkAutoMaskFreeImage ami(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883
reed41e010c2015-06-09 12:16:53 -0700884 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000885 SkBlitter* blitter = blitterChooser.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886
reed@google.com045e62d2011-10-24 12:19:46 +0000887 SkAAClipBlitterWrapper wrapper;
888 const SkRegion* clipRgn;
889
890 if (fRC->isBW()) {
891 clipRgn = &fRC->bwRgn();
892 } else {
893 wrapper.init(*fRC, blitter);
894 clipRgn = &wrapper.getRgn();
895 blitter = wrapper.getBlitter();
896 }
897 blitter->blitMaskRegion(*mask, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898}
899
reed@android.comebdeeb82009-09-03 21:45:49 +0000900static SkScalar fast_len(const SkVector& vec) {
901 SkScalar x = SkScalarAbs(vec.fX);
902 SkScalar y = SkScalarAbs(vec.fY);
903 if (x < y) {
904 SkTSwap(x, y);
905 }
906 return x + SkScalarHalf(y);
907}
908
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000909bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
910 SkScalar* coverage) {
911 SkASSERT(strokeWidth > 0);
912 // We need to try to fake a thick-stroke with a modulated hairline.
reed@google.comecadf992011-09-19 19:18:18 +0000913
tomhudson@google.com8d430182011-06-06 19:11:19 +0000914 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000915 return false;
916 }
reed@google.comecadf992011-09-19 19:18:18 +0000917
reed@android.comebdeeb82009-09-03 21:45:49 +0000918 SkVector src[2], dst[2];
reed@google.comecadf992011-09-19 19:18:18 +0000919 src[0].set(strokeWidth, 0);
920 src[1].set(0, strokeWidth);
reed@android.comebdeeb82009-09-03 21:45:49 +0000921 matrix.mapVectors(dst, src, 2);
922 SkScalar len0 = fast_len(dst[0]);
923 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000924 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
bsalomon49f085d2014-09-05 13:34:00 -0700925 if (coverage) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000926 *coverage = SkScalarAve(len0, len1);
927 }
reed@android.comebdeeb82009-09-03 21:45:49 +0000928 return true;
929 }
930 return false;
931}
932
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000933void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
934 SkDEBUGCODE(this->validate());
935
936 if (fRC->isEmpty()) {
937 return;
938 }
939
940 {
941 // TODO: Investigate optimizing these options. They are in the same
942 // order as SkDraw::drawPath, which handles each case. It may be
943 // that there is no way to optimize for these using the SkRRect path.
944 SkScalar coverage;
945 if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
946 goto DRAW_PATH;
947 }
948
949 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
950 goto DRAW_PATH;
951 }
952
953 if (paint.getRasterizer()) {
954 goto DRAW_PATH;
955 }
956 }
957
958 if (paint.getMaskFilter()) {
959 // Transform the rrect into device space.
960 SkRRect devRRect;
961 if (rrect.transform(*fMatrix, &devRRect)) {
reed41e010c2015-06-09 12:16:53 -0700962 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
bsalomon055e1922016-05-06 07:22:58 -0700963 if (paint.getMaskFilter()->filterRRect(devRRect, *fMatrix, *fRC, blitter.get())) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000964 return; // filterRRect() called the blitter, so we're done
965 }
966 }
967 }
968
969DRAW_PATH:
970 // Now fall back to the default case of using a path.
971 SkPath path;
972 path.addRRect(rrect);
halcanary96fcdcc2015-08-27 07:41:13 -0700973 this->drawPath(path, paint, nullptr, true);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000974}
975
caryclark1a7eb262016-01-21 07:07:02 -0800976SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
reed05d90442015-02-12 13:35:52 -0800977 if (!matrix.hasPerspective()) {
978 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
979 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
980 if (SkScalarsAreFinite(sx, sy)) {
lsalzmana4b87042016-07-06 12:19:56 -0700981 SkScalar scale = SkTMax(sx, sy);
982 if (scale > 0) {
983 return scale;
984 }
reed05d90442015-02-12 13:35:52 -0800985 }
986 }
987 return 1;
988}
989
reed82595b62016-05-09 17:48:46 -0700990void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
991 SkBlitter* customBlitter, bool doFill) const {
reed01a2ff82016-07-18 13:22:55 -0700992 // Do a conservative quick-reject test, since a looper or other modifier may have moved us
993 // out of range.
994 if (!devPath.isInverseFillType()) {
995 // If we're a H or V line, our bounds will be empty. So we bloat here just so we don't
996 // appear empty to the intersects call. This also gives us slop in case we're antialiasing
997 SkRect pathBounds = devPath.getBounds().makeOutset(1, 1);
998
999 if (paint.getMaskFilter()) {
1000 paint.getMaskFilter()->computeFastBounds(pathBounds, &pathBounds);
1001
1002 // Need to outset the path to work-around a bug in blurmaskfilter. When that is fixed
1003 // we can remove this hack. See skbug.com/5542
1004 pathBounds.outset(7, 7);
1005 }
1006
1007 // Now compare against the clip's bounds
1008 if (!SkRect::Make(fRC->getBounds()).intersects(pathBounds)) {
1009 return;
1010 }
1011 }
1012
reed82595b62016-05-09 17:48:46 -07001013 SkBlitter* blitter = nullptr;
1014 SkAutoBlitterChoose blitterStorage;
1015 if (nullptr == customBlitter) {
1016 blitterStorage.choose(fDst, *fMatrix, paint, drawCoverage);
1017 blitter = blitterStorage.get();
1018 } else {
1019 blitter = customBlitter;
1020 }
1021
1022 if (paint.getMaskFilter()) {
1023 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
1024 : SkStrokeRec::kHairline_InitStyle;
1025 if (paint.getMaskFilter()->filterPath(devPath, *fMatrix, *fRC, blitter, style)) {
1026 return; // filterPath() called the blitter, so we're done
1027 }
1028 }
1029
1030 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
1031 if (doFill) {
1032 if (paint.isAntiAlias()) {
1033 proc = SkScan::AntiFillPath;
1034 } else {
1035 proc = SkScan::FillPath;
1036 }
1037 } else { // hairline
1038 if (paint.isAntiAlias()) {
1039 switch (paint.getStrokeCap()) {
1040 case SkPaint::kButt_Cap:
1041 proc = SkScan::AntiHairPath;
1042 break;
1043 case SkPaint::kSquare_Cap:
1044 proc = SkScan::AntiHairSquarePath;
1045 break;
1046 case SkPaint::kRound_Cap:
1047 proc = SkScan::AntiHairRoundPath;
1048 break;
1049 default:
1050 proc SK_INIT_TO_AVOID_WARNING;
1051 SkDEBUGFAIL("unknown paint cap type");
1052 }
1053 } else {
1054 switch (paint.getStrokeCap()) {
1055 case SkPaint::kButt_Cap:
1056 proc = SkScan::HairPath;
1057 break;
1058 case SkPaint::kSquare_Cap:
1059 proc = SkScan::HairSquarePath;
1060 break;
1061 case SkPaint::kRound_Cap:
1062 proc = SkScan::HairRoundPath;
1063 break;
1064 default:
1065 proc SK_INIT_TO_AVOID_WARNING;
1066 SkDEBUGFAIL("unknown paint cap type");
1067 }
1068 }
1069 }
1070 proc(devPath, *fRC, blitter);
1071}
1072
reed@google.com32e5d972011-07-27 18:25:57 +00001073void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
reed@google.com126f7f52013-11-07 16:06:53 +00001074 const SkMatrix* prePathMatrix, bool pathIsMutable,
krajcevski53f09592014-08-06 11:12:14 -07001075 bool drawCoverage, SkBlitter* customBlitter) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001076 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077
1078 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001079 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 return;
1081 }
1082
1083 SkPath* pathPtr = (SkPath*)&origSrcPath;
1084 bool doFill = true;
1085 SkPath tmpPath;
1086 SkMatrix tmpMatrix;
1087 const SkMatrix* matrix = fMatrix;
jvanverthb3eb6872014-10-24 07:12:51 -07001088 tmpPath.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089
1090 if (prePathMatrix) {
reed@google.com32e5d972011-07-27 18:25:57 +00001091 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
1092 origPaint.getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +00001094
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 if (!pathIsMutable) {
1096 result = &tmpPath;
1097 pathIsMutable = true;
1098 }
1099 pathPtr->transform(*prePathMatrix, result);
1100 pathPtr = result;
1101 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001102 tmpMatrix.setConcat(*matrix, *prePathMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 matrix = &tmpMatrix;
1104 }
1105 }
1106 // at this point we're done with prePathMatrix
1107 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +00001108
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001109 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
reed@google.coma76de3d2011-01-13 18:30:42 +00001110
reed@google.comecadf992011-09-19 19:18:18 +00001111 {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001112 SkScalar coverage;
1113 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
1114 if (SK_Scalar1 == coverage) {
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001115 paint.writable()->setStrokeWidth(0);
reed374772b2016-10-05 17:33:02 -07001116 } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001117 U8CPU newAlpha;
1118#if 0
1119 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
1120 origPaint.getAlpha()));
1121#else
1122 // this is the old technique, which we preserve for now so
1123 // we don't change previous results (testing)
1124 // the new way seems fine, its just (a tiny bit) different
Mike Reeda99b6ce2017-02-04 11:04:26 -05001125 int scale = (int)(coverage * 256);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001126 newAlpha = origPaint.getAlpha() * scale >> 8;
1127#endif
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001128 SkPaint* writablePaint = paint.writable();
1129 writablePaint->setStrokeWidth(0);
1130 writablePaint->setAlpha(newAlpha);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001131 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 }
1133 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001134
reed@google.com32e5d972011-07-27 18:25:57 +00001135 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
reed@google.com4bbdeac2013-01-24 21:03:11 +00001136 SkRect cullRect;
halcanary96fcdcc2015-08-27 07:41:13 -07001137 const SkRect* cullRectPtr = nullptr;
reed@google.com4bbdeac2013-01-24 21:03:11 +00001138 if (this->computeConservativeLocalClipBounds(&cullRect)) {
1139 cullRectPtr = &cullRect;
1140 }
reed05d90442015-02-12 13:35:52 -08001141 doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr,
caryclark1a7eb262016-01-21 07:07:02 -08001142 ComputeResScaleForStroking(*fMatrix));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 pathPtr = &tmpPath;
1144 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001145
reed@google.com32e5d972011-07-27 18:25:57 +00001146 if (paint->getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 SkMask mask;
reed@google.com32e5d972011-07-27 18:25:57 +00001148 if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
reed@google.com045e62d2011-10-24 12:19:46 +00001149 &fRC->getBounds(), paint->getMaskFilter(), &mask,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
reed@google.com32e5d972011-07-27 18:25:57 +00001151 this->drawDevMask(mask, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 SkMask::FreeImage(mask.fImage);
1153 }
1154 return;
1155 }
1156
1157 // avoid possibly allocating a new path in transform if we can
1158 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1159
1160 // transform the path into device space
1161 pathPtr->transform(*matrix, devPathPtr);
1162
reed82595b62016-05-09 17:48:46 -07001163 this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164}
1165
reed82595b62016-05-09 17:48:46 -07001166void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {
reed@google.com900ecf22014-02-20 20:55:37 +00001167 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168
fmalitac7e211a2016-01-07 10:34:46 -08001169 if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) {
reed@google.come1ca7052013-12-17 19:22:07 +00001170 int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
1171 int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172
reed92fc2ae2015-05-22 08:06:21 -07001173 SkAutoPixmapUnlock result;
1174 if (!bitmap.requestLock(&result)) {
reed@google.coma641f3f2012-12-13 22:16:30 +00001175 return;
1176 }
reed92fc2ae2015-05-22 08:06:21 -07001177 const SkPixmap& pmap = result.pixmap();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 SkMask mask;
reed92fc2ae2015-05-22 08:06:21 -07001179 mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 mask.fFormat = SkMask::kA8_Format;
reed92fc2ae2015-05-22 08:06:21 -07001181 mask.fRowBytes = SkToU32(pmap.rowBytes());
1182 // fImage is typed as writable, but in this case it is used read-only
1183 mask.fImage = (uint8_t*)pmap.addr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001184
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 this->drawDevMask(mask, paint);
1186 } else { // need to xform the bitmap first
1187 SkRect r;
1188 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001189
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 r.set(0, 0,
1191 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1192 fMatrix->mapRect(&r);
1193 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001194
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 // set the mask's bounds to the transformed bitmap-bounds,
1196 // clipped to the actual device
1197 {
1198 SkIRect devBounds;
reed41e010c2015-06-09 12:16:53 -07001199 devBounds.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 // need intersect(l, t, r, b) on irect
1201 if (!mask.fBounds.intersect(devBounds)) {
1202 return;
1203 }
1204 }
reed@android.com543ed932009-04-24 12:43:40 +00001205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 mask.fFormat = SkMask::kA8_Format;
1207 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001208 size_t size = mask.computeImageSize();
1209 if (0 == size) {
1210 // the mask is too big to allocated, draw nothing
1211 return;
1212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213
1214 // allocate (and clear) our temp buffer to hold the transformed bitmap
scroggo565901d2015-12-10 10:44:13 -08001215 SkAutoTMalloc<uint8_t> storage(size);
1216 mask.fImage = storage.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001218
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1220 {
1221 SkBitmap device;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001222 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
1223 mask.fImage, mask.fRowBytes);
reed@google.coma76de3d2011-01-13 18:30:42 +00001224
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 SkCanvas c(device);
1226 // need the unclipped top/left for the translate
1227 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1228 -SkIntToScalar(mask.fBounds.fTop));
1229 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001230
1231 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001232 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001233 SkPaint tmpPaint;
1234 tmpPaint.setFlags(paint.getFlags());
brianosman66a96d02016-05-19 08:43:55 -07001235 tmpPaint.setFilterQuality(paint.getFilterQuality());
Herb Derby4bf560a2017-02-14 11:13:26 -05001236 SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap);
reed@android.com3469c762009-02-24 19:03:20 +00001237 SkRect rr;
1238 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1239 SkIntToScalar(bitmap.height()));
Herb Derby4bf560a2017-02-14 11:13:26 -05001240 c.drawRect(rr, paintWithShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 }
1242 this->drawDevMask(mask, paint);
1243 }
1244}
1245
reed@google.com045e62d2011-10-24 12:19:46 +00001246static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 const SkRect& srcR) {
1248 SkRect dstR;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 m.mapRect(&dstR, srcR);
reedb07a94f2014-11-19 05:03:18 -08001250 return c.quickReject(dstR.roundOut());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251}
1252
reed@google.com045e62d2011-10-24 12:19:46 +00001253static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 int width, int height) {
1255 SkRect r;
1256 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1257 return clipped_out(matrix, clip, r);
1258}
1259
reedc240e712015-05-23 12:26:41 -07001260static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
1261 return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
reed@google.com045e62d2011-10-24 12:19:46 +00001262}
1263
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed03939122014-12-15 13:42:51 -08001265 const SkRect* dstBounds, 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
fmalitaf85d2a42016-10-03 13:46:58 -07001275 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1276 if (origPaint.getStyle() != SkPaint::kFill_Style) {
1277 paint.writable()->setStyle(SkPaint::kFill_Style);
1278 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 SkMatrix matrix;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001281 matrix.setConcat(*fMatrix, prematrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282
reed@google.com045e62d2011-10-24 12:19:46 +00001283 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 return;
1285 }
1286
fmalitac7e211a2016-01-07 10:34:46 -08001287 if (bitmap.colorType() != kAlpha_8_SkColorType
fmalitaf85d2a42016-10-03 13:46:58 -07001288 && SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001289 //
1290 // It is safe to call lock pixels now, since we know the matrix is
1291 // (more or less) identity.
1292 //
reedc240e712015-05-23 12:26:41 -07001293 SkAutoPixmapUnlock unlocker;
1294 if (!bitmap.requestLock(&unlocker)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001295 return;
1296 }
reedc240e712015-05-23 12:26:41 -07001297 const SkPixmap& pmap = unlocker.pixmap();
reed@google.come1ca7052013-12-17 19:22:07 +00001298 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1299 int iy = SkScalarRoundToInt(matrix.getTranslateY());
reedc240e712015-05-23 12:26:41 -07001300 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
Herb Derby57bfa022017-02-09 17:25:43 -05001301 char storage[kSkBlitterContextSize];
1302 SkArenaAlloc allocator{storage};
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001303 // blitter will be owned by the allocator.
fmalitaf85d2a42016-10-03 13:46:58 -07001304 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator);
reed@google.com045e62d2011-10-24 12:19:46 +00001305 if (blitter) {
reedc240e712015-05-23 12:26:41 -07001306 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
1307 *fRC, blitter);
reed@google.com045e62d2011-10-24 12:19:46 +00001308 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 }
reedc240e712015-05-23 12:26:41 -07001310 // if !blitter, then we fall-through to the slower case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001313
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 // now make a temp draw on the stack, and use it
1315 //
1316 SkDraw draw(*this);
1317 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001318
Matt Sarett70ac8a92016-11-03 10:55:08 -04001319 if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
fmalitaf85d2a42016-10-03 13:46:58 -07001320 draw.drawBitmapAsMask(bitmap, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 } else {
Herb Derby4bf560a2017-02-14 11:13:26 -05001322 SkPaint paintWithShader = make_paint_with_image(*paint, bitmap);
reed03939122014-12-15 13:42:51 -08001323 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1324 if (dstBounds) {
1325 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1326 } else {
1327 draw.drawRect(srcBounds, paintWithShader);
1328 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 }
1330}
1331
reedc240e712015-05-23 12:26:41 -07001332void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001333 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001334
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001336 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001338 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 return;
1340 }
1341
reedc240e712015-05-23 12:26:41 -07001342 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343
reed@google.com045e62d2011-10-24 12:19:46 +00001344 if (fRC->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 return; // nothing to draw
1346 }
1347
reed@google.com40c2ba22011-07-25 19:41:22 +00001348 SkPaint paint(origPaint);
1349 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350
reedc240e712015-05-23 12:26:41 -07001351 SkAutoPixmapUnlock unlocker;
1352 if (!bitmap.requestLock(&unlocker)) {
1353 return;
1354 }
1355 const SkPixmap& pmap = unlocker.pixmap();
1356
halcanary96fcdcc2015-08-27 07:41:13 -07001357 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001358 // blitter will be owned by the allocator.
Herb Derby57bfa022017-02-09 17:25:43 -05001359 char storage[kSkBlitterContextSize];
1360 SkArenaAlloc allocator{storage};
reed41e010c2015-06-09 12:16:53 -07001361 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 if (blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +00001363 SkScan::FillIRect(bounds, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 return;
1365 }
1366 }
1367
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 SkMatrix matrix;
1369 SkRect r;
1370
1371 // get a scalar version of our rect
1372 r.set(bounds);
1373
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001374 // create shader with offset
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 matrix.setTranslate(r.fLeft, r.fTop);
Herb Derby4bf560a2017-02-14 11:13:26 -05001376 SkPaint paintWithShader = make_paint_with_image(paint, bitmap, &matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 SkDraw draw(*this);
1378 matrix.reset();
1379 draw.fMatrix = &matrix;
1380 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001381 // is this OK if paint has a rasterizer?
Herb Derby4bf560a2017-02-14 11:13:26 -05001382 draw.drawRect(r, paintWithShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383}
1384
1385///////////////////////////////////////////////////////////////////////////////
1386
1387#include "SkScalerContext.h"
1388#include "SkGlyphCache.h"
reed@google.come6913762012-08-07 15:19:47 +00001389#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390#include "SkUtils.h"
1391
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001392bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
1393 // hairline glyphs are fast enough so we don't need to cache them
1394 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
1395 return true;
1396 }
1397
1398 // we don't cache perspective
1399 if (ctm.hasPerspective()) {
1400 return true;
1401 }
1402
1403 SkMatrix textM;
1404 return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM));
1405}
1406
Mike Reed99330ba2017-02-22 11:01:08 -05001407void SkDraw::drawText_asPaths(const char text[], size_t byteLength, SkScalar x, SkScalar y,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001409 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
djsollen@google.com166e6532012-03-20 14:24:38 +00001411 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
1413 SkMatrix matrix;
1414 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1415 matrix.postTranslate(x, y);
1416
1417 const SkPath* iterPath;
1418 SkScalar xpos, prevXPos = 0;
1419
reed@google.com7b4531f2012-08-07 15:53:00 +00001420 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001422 if (iterPath) {
Mike Reeda1361362017-03-07 09:37:29 -05001423 this->drawPath(*iterPath, iter.getPaint(), &matrix, false);
reed@android.comf2b98d62010-12-20 18:26:13 +00001424 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 prevXPos = xpos;
1426 }
1427}
1428
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429// disable warning : local variable used without having been initialized
bungemand7dc76f2016-03-10 11:14:40 -08001430#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431#pragma warning ( push )
1432#pragma warning ( disable : 4701 )
1433#endif
1434
herbf553e4e2015-11-09 08:51:56 -08001435////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436
herbd4c24f62015-12-07 12:12:29 -08001437class DrawOneGlyph {
1438public:
1439 DrawOneGlyph(const SkDraw& draw, const SkPaint& paint, SkGlyphCache* cache, SkBlitter* blitter)
1440 : fUseRegionToDraw(UsingRegionToDraw(draw.fRC))
1441 , fGlyphCache(cache)
1442 , fBlitter(blitter)
1443 , fClip(fUseRegionToDraw ? &draw.fRC->bwRgn() : nullptr)
1444 , fDraw(draw)
1445 , fPaint(paint)
1446 , fClipBounds(PickClipBounds(draw)) { }
herb11a7f7f2015-11-24 12:41:00 -08001447
herbd4c24f62015-12-07 12:12:29 -08001448 void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
1449 position += rounding;
herbd4c24f62015-12-07 12:12:29 -08001450 // Prevent glyphs from being drawn outside of or straddling the edge of device space.
mtklein875e13c2016-06-19 05:28:33 -07001451 // Comparisons written a little weirdly so that NaN coordinates are treated safely.
1452 auto gt = [](float a, int b) { return !(a <= (float)b); };
1453 auto lt = [](float a, int b) { return !(a >= (float)b); };
1454 if (gt(position.fX, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1455 lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
1456 gt(position.fY, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1457 lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))) {
herbd4c24f62015-12-07 12:12:29 -08001458 return;
1459 }
herb11a7f7f2015-11-24 12:41:00 -08001460
benjaminwagner77a9cc12016-03-25 11:24:30 -07001461 int left = SkScalarFloorToInt(position.fX);
1462 int top = SkScalarFloorToInt(position.fY);
herbd4c24f62015-12-07 12:12:29 -08001463 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1464
1465 left += glyph.fLeft;
1466 top += glyph.fTop;
1467
1468 int right = left + glyph.fWidth;
1469 int bottom = top + glyph.fHeight;
1470
1471 SkMask mask;
1472 mask.fBounds.set(left, top, right, bottom);
mtklein875e13c2016-06-19 05:28:33 -07001473 SkASSERT(!mask.fBounds.isEmpty());
herbd4c24f62015-12-07 12:12:29 -08001474
1475 if (fUseRegionToDraw) {
1476 SkRegion::Cliperator clipper(*fClip, mask.fBounds);
1477
1478 if (!clipper.done() && this->getImageData(glyph, &mask)) {
1479 const SkIRect& cr = clipper.rect();
1480 do {
1481 this->blitMask(mask, cr);
1482 clipper.next();
1483 } while (!clipper.done());
1484 }
1485 } else {
1486 SkIRect storage;
1487 SkIRect* bounds = &mask.fBounds;
1488
1489 // this extra test is worth it, assuming that most of the time it succeeds
1490 // since we can avoid writing to storage
1491 if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) {
1492 if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds))
1493 return;
1494 bounds = &storage;
1495 }
1496
1497 if (this->getImageData(glyph, &mask)) {
1498 this->blitMask(mask, *bounds);
1499 }
1500 }
1501 }
1502
1503private:
1504 static bool UsingRegionToDraw(const SkRasterClip* rClip) {
1505 return rClip->isBW() && !rClip->isRect();
1506 }
1507
1508 static SkIRect PickClipBounds(const SkDraw& draw) {
1509 const SkRasterClip& rasterClip = *draw.fRC;
1510
1511 if (rasterClip.isBW()) {
1512 return rasterClip.bwRgn().getBounds();
1513 } else {
1514 return rasterClip.aaRgn().getBounds();
1515 }
1516 }
1517
1518 bool getImageData(const SkGlyph& glyph, SkMask* mask) {
1519 uint8_t* bits = (uint8_t*)(fGlyphCache->findImage(glyph));
1520 if (nullptr == bits) {
1521 return false; // can't rasterize glyph
1522 }
1523 mask->fImage = bits;
1524 mask->fRowBytes = glyph.rowBytes();
1525 mask->fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1526 return true;
1527 }
1528
herb11a7f7f2015-11-24 12:41:00 -08001529 void blitMask(const SkMask& mask, const SkIRect& clip) const {
1530 if (SkMask::kARGB32_Format == mask.fFormat) {
herbd4c24f62015-12-07 12:12:29 -08001531 SkBitmap bm;
1532 bm.installPixels(
1533 SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()),
1534 (SkPMColor*)mask.fImage, mask.fRowBytes);
1535
1536 fDraw.drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), fPaint);
herb11a7f7f2015-11-24 12:41:00 -08001537 } else {
1538 fBlitter->blitMask(mask, clip);
1539 }
1540 }
1541
herbd4c24f62015-12-07 12:12:29 -08001542 const bool fUseRegionToDraw;
1543 SkGlyphCache * const fGlyphCache;
1544 SkBlitter * const fBlitter;
1545 const SkRegion* const fClip;
1546 const SkDraw& fDraw;
1547 const SkPaint& fPaint;
1548 const SkIRect fClipBounds;
herb11a7f7f2015-11-24 12:41:00 -08001549};
1550
herbd4c24f62015-12-07 12:12:29 -08001551////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552
brianosmana1e8f8d2016-04-08 06:47:54 -07001553uint32_t SkDraw::scalerContextFlags() const {
1554 uint32_t flags = SkPaint::kBoostContrast_ScalerContextFlag;
Mike Reed99330ba2017-02-22 11:01:08 -05001555 if (!fDst.colorSpace()) {
brianosmana1e8f8d2016-04-08 06:47:54 -07001556 flags |= SkPaint::kFakeGamma_ScalerContextFlag;
1557 }
1558 return flags;
bungemanf6d1e602016-02-22 13:20:28 -08001559}
1560
Mike Reed99330ba2017-02-22 11:01:08 -05001561void SkDraw::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y,
1562 const SkPaint& paint, const SkSurfaceProps* props) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001563 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564
reed@android.comf2b98d62010-12-20 18:26:13 +00001565 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566
1567 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001568 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 return;
1570 }
1571
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001572 // SkScalarRec doesn't currently have a way of representing hairline stroke and
1573 // will fill if its frame-width is 0.
reed@google.comed43dff2013-06-04 16:56:27 +00001574 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 this->drawText_asPaths(text, byteLength, x, y, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576 return;
1577 }
1578
Mike Reed99330ba2017-02-22 11:01:08 -05001579 SkAutoGlyphCache cache(paint, props, this->scalerContextFlags(), fMatrix);
herb11a7f7f2015-11-24 12:41:00 -08001580
1581 // The Blitter Choose needs to be live while using the blitter below.
1582 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1583 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001584 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585
herbe59124e2015-11-18 10:54:39 -08001586 SkFindAndPlaceGlyph::ProcessText(
herb4c11b3f2015-11-20 13:53:12 -08001587 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001588 {x, y}, *fMatrix, paint.getTextAlign(), cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589}
1590
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591//////////////////////////////////////////////////////////////////////////////
1592
Mike Reed99330ba2017-02-22 11:01:08 -05001593void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength, const SkScalar pos[],
1594 int scalarsPerPosition, const SkPoint& offset,
1595 const SkPaint& origPaint, const SkSurfaceProps* props) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001596 // setup our std paint, in hopes of getting hits in the cache
1597 SkPaint paint(origPaint);
1598 SkScalar matrixScale = paint.setupForAsPaths();
1599
reed@google.com5a649022013-06-05 18:00:30 +00001600 SkMatrix matrix;
1601 matrix.setScale(matrixScale, matrixScale);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001602
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001603 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
1604 paint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07001605 paint.setPathEffect(nullptr);
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001606
robertphillipse34f17d2016-07-19 07:59:22 -07001607 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
1608 paint.isDevKernText(),
1609 true);
Mike Reed99330ba2017-02-22 11:01:08 -05001610 SkAutoGlyphCache cache(paint, props, this->scalerContextFlags(), nullptr);
reed@google.comed43dff2013-06-04 16:56:27 +00001611
1612 const char* stop = text + byteLength;
bungeman79738cc2015-03-11 14:05:29 -07001613 SkTextAlignProc alignProc(paint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -07001614 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001615
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001616 // Now restore the original settings, so we "draw" with whatever style/stroking.
1617 paint.setStyle(origPaint.getStyle());
Mike Reed693fdbd2017-01-12 10:13:40 -05001618 paint.setPathEffect(origPaint.refPathEffect());
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001619
reed@google.comed43dff2013-06-04 16:56:27 +00001620 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -08001621 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
reed@google.comed43dff2013-06-04 16:56:27 +00001622 if (glyph.fWidth) {
1623 const SkPath* path = cache->findPath(glyph);
1624 if (path) {
kkinnunencb9a2c82014-06-12 23:06:28 -07001625 SkPoint tmsLoc;
1626 tmsProc(pos, &tmsLoc);
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001627 SkPoint loc;
kkinnunencb9a2c82014-06-12 23:06:28 -07001628 alignProc(tmsLoc, glyph, &loc);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001629
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001630 matrix[SkMatrix::kMTransX] = loc.fX;
1631 matrix[SkMatrix::kMTransY] = loc.fY;
Mike Reed99330ba2017-02-22 11:01:08 -05001632 this->drawPath(*path, paint, &matrix, false);
reed@google.comed43dff2013-06-04 16:56:27 +00001633 }
1634 }
1635 pos += scalarsPerPosition;
1636 }
1637}
1638
Mike Reed99330ba2017-02-22 11:01:08 -05001639void SkDraw::drawPosText(const char text[], size_t byteLength, const SkScalar pos[],
1640 int scalarsPerPosition, const SkPoint& offset, const SkPaint& paint,
1641 const SkSurfaceProps* props) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001642 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1644
reed@android.comf2b98d62010-12-20 18:26:13 +00001645 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646
1647 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001648 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649 return;
1650 }
1651
reed@google.comed43dff2013-06-04 16:56:27 +00001652 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
Mike Reed99330ba2017-02-22 11:01:08 -05001653 this->drawPosText_asPaths(text, byteLength, pos, scalarsPerPosition, offset, paint, props);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654 return;
1655 }
1656
Mike Reed99330ba2017-02-22 11:01:08 -05001657 SkAutoGlyphCache cache(paint, props, this->scalerContextFlags(), fMatrix);
herbd4c24f62015-12-07 12:12:29 -08001658
herb9be5ff62015-11-11 11:30:11 -08001659 // The Blitter Choose needs to be live while using the blitter below.
herb11a7f7f2015-11-24 12:41:00 -08001660 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1661 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001662 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
herbd4c24f62015-12-07 12:12:29 -08001663 SkPaint::Align textAlignment = paint.getTextAlign();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664
herb9be5ff62015-11-11 11:30:11 -08001665 SkFindAndPlaceGlyph::ProcessPosText(
herb4c11b3f2015-11-20 13:53:12 -08001666 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001667 offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668}
1669
bungemand7dc76f2016-03-10 11:14:40 -08001670#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671#pragma warning ( pop )
1672#endif
1673
1674///////////////////////////////////////////////////////////////////////////////
1675
reed5dc6b7d2015-04-14 10:40:44 -07001676static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
1678}
1679
1680static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
1681 const SkPoint texs[], SkMatrix* matrix) {
1682 SkPoint src[3], dst[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00001683
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684 src[0] = texs[state.f0];
1685 src[1] = texs[state.f1];
1686 src[2] = texs[state.f2];
1687 dst[0] = verts[state.f0];
1688 dst[1] = verts[state.f1];
1689 dst[2] = verts[state.f2];
1690 return matrix->setPolyToPoly(src, dst, 3);
1691}
1692
1693class SkTriColorShader : public SkShader {
1694public:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001695 SkTriColorShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001696
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001697 class TriColorShaderContext : public SkShader::Context {
1698 public:
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001699 TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001700 virtual ~TriColorShaderContext();
mtklein36352bf2015-03-25 18:17:31 -07001701 void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001702
1703 private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001704 bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
1705
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001706 SkMatrix fDstToUnit;
1707 SkPMColor fColors[3];
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001708 bool fSetup;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001709
1710 typedef SkShader::Context INHERITED;
1711 };
reed@google.coma76de3d2011-01-13 18:30:42 +00001712
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001713 struct TriColorShaderData {
1714 const SkPoint* pts;
1715 const SkColor* colors;
1716 const VertState *state;
1717 };
1718
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001719 SK_TO_STRING_OVERRIDE()
mtklein7e44bb12015-01-07 09:06:08 -08001720
1721 // For serialization. This will never be called.
halcanary96fcdcc2015-08-27 07:41:13 -07001722 Factory getFactory() const override { sk_throw(); return nullptr; }
djsollen@google.comba28d032012-03-26 17:57:35 +00001723
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001724 // Supply setup data to context from drawing setup
1725 void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; }
1726
1727 // Take the setup data from context when needed.
1728 TriColorShaderData* takeSetupData() {
1729 TriColorShaderData *data = fSetupData;
1730 fSetupData = NULL;
1731 return data;
1732 }
1733
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734protected:
Herb Derby83e939b2017-02-07 14:25:11 -05001735 Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
1736 return alloc->make<TriColorShaderContext>(*this, rec);
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +00001737 }
1738
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001740 TriColorShaderData *fSetupData;
1741
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742 typedef SkShader INHERITED;
1743};
1744
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001745bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[],
1746 int index0, int index1, int index2) {
reed@google.coma76de3d2011-01-13 18:30:42 +00001747
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 fColors[0] = SkPreMultiplyColor(colors[index0]);
1749 fColors[1] = SkPreMultiplyColor(colors[index1]);
1750 fColors[2] = SkPreMultiplyColor(colors[index2]);
reed@google.coma76de3d2011-01-13 18:30:42 +00001751
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752 SkMatrix m, im;
1753 m.reset();
1754 m.set(0, pts[index1].fX - pts[index0].fX);
1755 m.set(1, pts[index2].fX - pts[index0].fX);
1756 m.set(2, pts[index0].fX);
1757 m.set(3, pts[index1].fY - pts[index0].fY);
1758 m.set(4, pts[index2].fY - pts[index0].fY);
1759 m.set(5, pts[index0].fY);
1760 if (!m.invert(&im)) {
1761 return false;
1762 }
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001763 // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix
1764 // as our interators are intrinsically tied to the vertices, and nothing else.
1765 SkMatrix ctmInv;
1766 if (!this->getCTM().invert(&ctmInv)) {
1767 return false;
1768 }
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001769 // TODO replace INV(m) * INV(ctm) with INV(ctm * m)
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001770 fDstToUnit.setConcat(im, ctmInv);
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001771 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772}
1773
1774#include "SkColorPriv.h"
reed@android.com689411a2008-12-18 02:52:32 +00001775#include "SkComposeShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776
1777static int ScalarTo256(SkScalar v) {
benjaminwagner43437c22016-02-24 10:57:47 -08001778 return static_cast<int>(SkScalarPin(v, 0, 1) * 256 + 0.5);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779}
1780
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001781SkTriColorShader::SkTriColorShader()
1782 : INHERITED(NULL)
1783 , fSetupData(NULL) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001784
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001785SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader,
1786 const ContextRec& rec)
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001787 : INHERITED(shader, rec)
1788 , fSetup(false) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001789
1790SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {}
1791
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001792void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001793 SkTriColorShader* parent = static_cast<SkTriColorShader*>(const_cast<SkShader*>(&fShader));
1794 TriColorShaderData* set = parent->takeSetupData();
1795 if (set) {
1796 fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2);
1797 }
1798
1799 if (!fSetup) {
1800 // Invalid matrices. Not checked before so no need to assert.
1801 return;
1802 }
1803
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001804 const int alphaScale = Sk255To256(this->getPaintAlpha());
1805
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806 SkPoint src;
reed@google.coma76de3d2011-01-13 18:30:42 +00001807
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 for (int i = 0; i < count; i++) {
1809 fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
1810 x += 1;
reed@google.coma76de3d2011-01-13 18:30:42 +00001811
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 int scale1 = ScalarTo256(src.fX);
1813 int scale2 = ScalarTo256(src.fY);
1814 int scale0 = 256 - scale1 - scale2;
1815 if (scale0 < 0) {
1816 if (scale1 > scale2) {
1817 scale2 = 256 - scale1;
1818 } else {
1819 scale1 = 256 - scale2;
1820 }
1821 scale0 = 0;
1822 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001823
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001824 if (256 != alphaScale) {
1825 scale0 = SkAlphaMul(scale0, alphaScale);
1826 scale1 = SkAlphaMul(scale1, alphaScale);
1827 scale2 = SkAlphaMul(scale2, alphaScale);
1828 }
1829
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830 dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001831 SkAlphaMulQ(fColors[1], scale1) +
1832 SkAlphaMulQ(fColors[2], scale2);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 }
1834}
1835
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001836#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001837void SkTriColorShader::toString(SkString* str) const {
1838 str->append("SkTriColorShader: (");
1839
1840 this->INHERITED::toString(str);
1841
1842 str->append(")");
1843}
1844#endif
1845
Florin Malitaf614ba22017-02-16 22:12:41 -05001846namespace {
1847
1848// Similar to SkLocalMatrixShader, but composes the local matrix with the CTM (instead
1849// of composing with the inherited local matrix):
1850//
1851// rec' = {rec.ctm x localMatrix, rec.localMatrix}
1852//
1853// (as opposed to rec' = {rec.ctm, rec.localMatrix x localMatrix})
1854//
1855class SkLocalInnerMatrixShader final : public SkShader {
1856public:
1857 SkLocalInnerMatrixShader(sk_sp<SkShader> proxy, const SkMatrix& localMatrix)
1858 : INHERITED(&localMatrix)
1859 , fProxyShader(std::move(proxy)) {}
1860
1861 Factory getFactory() const override {
1862 SkASSERT(false);
1863 return nullptr;
1864 }
1865
1866protected:
1867 void flatten(SkWriteBuffer&) const override {
1868 SkASSERT(false);
1869 }
1870
1871 Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
1872 SkMatrix adjustedCTM = SkMatrix::Concat(*rec.fMatrix, this->getLocalMatrix());
1873 ContextRec newRec(rec);
1874 newRec.fMatrix = &adjustedCTM;
1875 return fProxyShader->makeContext(newRec, alloc);
1876 }
1877
1878 bool onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
1879 const SkMatrix& ctm, const SkPaint& paint,
1880 const SkMatrix* localM) const override {
1881 // We control the shader graph ancestors, so we know there's no local matrix being
1882 // injected before this.
1883 SkASSERT(!localM);
1884
1885 SkMatrix adjustedCTM = SkMatrix::Concat(ctm, this->getLocalMatrix());
1886 return fProxyShader->appendStages(p, cs, alloc, adjustedCTM, paint);
1887 }
1888
1889private:
1890 sk_sp<SkShader> fProxyShader;
1891
1892 typedef SkShader INHERITED;
1893};
1894
1895sk_sp<SkShader> MakeTextureShader(const VertState& state, const SkPoint verts[],
Florin Malita4b665122017-02-14 16:19:49 -05001896 const SkPoint texs[], const SkPaint& paint,
1897 SkColorSpace* dstColorSpace,
1898 SkArenaAlloc* alloc) {
1899 SkASSERT(paint.getShader());
1900
1901 const auto& p0 = texs[state.f0],
1902 p1 = texs[state.f1],
1903 p2 = texs[state.f2];
1904
1905 if (p0 != p1 || p0 != p2) {
1906 // Common case (non-collapsed texture coordinates).
1907 // Map the texture to vertices using a local transform.
Florin Malitaf614ba22017-02-16 22:12:41 -05001908
1909 // We cannot use a plain SkLocalMatrix shader, because we need the texture matrix
1910 // to compose next to the CTM.
Florin Malita4b665122017-02-14 16:19:49 -05001911 SkMatrix localMatrix;
1912 return texture_to_matrix(state, verts, texs, &localMatrix)
Florin Malitaf614ba22017-02-16 22:12:41 -05001913 ? alloc->makeSkSp<SkLocalInnerMatrixShader>(paint.refShader(), localMatrix)
Florin Malita4b665122017-02-14 16:19:49 -05001914 : nullptr;
1915 }
1916
1917 // Collapsed texture coordinates special case.
1918 // The texture is a solid color, sampled at the given point.
1919 SkMatrix shaderInvLocalMatrix;
1920 SkAssertResult(paint.getShader()->getLocalMatrix().invert(&shaderInvLocalMatrix));
1921
1922 const auto sample = SkPoint::Make(0.5f, 0.5f);
1923 const auto mappedSample = shaderInvLocalMatrix.mapXY(sample.x(), sample.y()),
1924 mappedPoint = shaderInvLocalMatrix.mapXY(p0.x(), p0.y());
1925 const auto localMatrix = SkMatrix::MakeTrans(mappedSample.x() - mappedPoint.x(),
1926 mappedSample.y() - mappedPoint.y());
1927
1928 SkShader::ContextRec rec(paint, SkMatrix::I(), &localMatrix,
1929 SkShader::ContextRec::kPMColor_DstType, dstColorSpace);
1930 auto* ctx = paint.getShader()->makeContext(rec, alloc);
1931 if (!ctx) {
1932 return nullptr;
1933 }
1934
1935 SkPMColor pmColor;
1936 ctx->shadeSpan(SkScalarFloorToInt(sample.x()), SkScalarFloorToInt(sample.y()), &pmColor, 1);
1937
1938 // no need to keep this temp context around.
1939 alloc->reset();
1940
1941 return alloc->makeSkSp<SkColorShader>(SkUnPreMultiply::PMColorToColor(pmColor));
1942}
1943
Florin Malitaf614ba22017-02-16 22:12:41 -05001944} // anonymous ns
1945
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
1947 const SkPoint vertices[], const SkPoint textures[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001948 const SkColor colors[], SkBlendMode bmode,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949 const uint16_t indices[], int indexCount,
1950 const SkPaint& paint) const {
bsalomon49f085d2014-09-05 13:34:00 -07001951 SkASSERT(0 == count || vertices);
reed@google.coma76de3d2011-01-13 18:30:42 +00001952
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953 // abort early if there is nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001954 if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955 return;
1956 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001957
reed@android.com8a1c16f2008-12-17 15:59:43 +00001958 // transform out vertices into device coordinates
1959 SkAutoSTMalloc<16, SkPoint> storage(count);
1960 SkPoint* devVerts = storage.get();
1961 fMatrix->mapPoints(devVerts, vertices, count);
reed@google.coma76de3d2011-01-13 18:30:42 +00001962
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963 /*
1964 We can draw the vertices in 1 of 4 ways:
1965
1966 - solid color (no shader/texture[], no colors[])
1967 - just colors (no shader/texture[], has colors[])
1968 - just texture (has shader/texture[], no colors[])
1969 - colors * texture (has shader/texture[], has colors[])
reed@google.coma76de3d2011-01-13 18:30:42 +00001970
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971 Thus for texture drawing, we need both texture[] and a shader.
1972 */
1973
reed8a21c9f2016-03-08 18:50:00 -08001974 auto triShader = sk_make_sp<SkTriColorShader>();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975 SkPaint p(paint);
1976
1977 SkShader* shader = p.getShader();
halcanary96fcdcc2015-08-27 07:41:13 -07001978 if (nullptr == shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 // if we have no shader, we ignore the texture coordinates
halcanary96fcdcc2015-08-27 07:41:13 -07001980 textures = nullptr;
1981 } else if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982 // if we don't have texture coordinates, ignore the shader
halcanary96fcdcc2015-08-27 07:41:13 -07001983 p.setShader(nullptr);
1984 shader = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985 }
1986
1987 // setup the custom shader (if needed)
bsalomon49f085d2014-09-05 13:34:00 -07001988 if (colors) {
halcanary96fcdcc2015-08-27 07:41:13 -07001989 if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990 // just colors (no texture)
reed8a21c9f2016-03-08 18:50:00 -08001991 p.setShader(triShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992 } else {
1993 // colors * texture
1994 SkASSERT(shader);
Mike Reed7d954ad2016-10-28 15:42:34 -04001995 p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), bmode));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 }
1997 }
1998
reed41e010c2015-06-09 12:16:53 -07001999 SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002000 // Abort early if we failed to create a shader context.
reed@google.comea033602012-12-14 13:13:55 +00002001 if (blitter->isNullBlitter()) {
2002 return;
2003 }
2004
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005 // setup our state and function pointer for iterating triangles
2006 VertState state(count, indices, indexCount);
2007 VertState::Proc vertProc = state.chooseProc(vmode);
reed@google.coma76de3d2011-01-13 18:30:42 +00002008
bsalomon49f085d2014-09-05 13:34:00 -07002009 if (textures || colors) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08002010 SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state };
2011
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 while (vertProc(&state)) {
Florin Malita00dca8c2017-01-27 12:05:27 -05002013 auto* blitterPtr = blitter.get();
2014
Florin Malita4b665122017-02-14 16:19:49 -05002015 // We're going to allocate at most
2016 //
2017 // * one SkLocalMatrixShader OR one SkColorShader
2018 // * one SkComposeShader
2019 // * one SkAutoBlitterChoose
2020 //
2021 static constexpr size_t kAllocSize =
2022 sizeof(SkAutoBlitterChoose) + sizeof(SkComposeShader) +
Florin Malitaf614ba22017-02-16 22:12:41 -05002023 SkTMax(sizeof(SkLocalInnerMatrixShader), sizeof(SkColorShader));
Florin Malita4b665122017-02-14 16:19:49 -05002024 char allocBuffer[kAllocSize];
2025 SkArenaAlloc alloc(allocBuffer);
Florin Malita00dca8c2017-01-27 12:05:27 -05002026
bsalomon49f085d2014-09-05 13:34:00 -07002027 if (textures) {
Florin Malita4b665122017-02-14 16:19:49 -05002028 sk_sp<SkShader> texShader = MakeTextureShader(state, vertices, textures, paint,
2029 fDst.colorSpace(), &alloc);
2030 if (texShader) {
Florin Malita00dca8c2017-01-27 12:05:27 -05002031 SkPaint localPaint(p);
Florin Malita4b665122017-02-14 16:19:49 -05002032 localPaint.setShader(colors
2033 ? alloc.makeSkSp<SkComposeShader>(triShader, std::move(texShader), bmode)
2034 : std::move(texShader));
Florin Malita00dca8c2017-01-27 12:05:27 -05002035
Florin Malita4b665122017-02-14 16:19:49 -05002036 blitterPtr = alloc.make<SkAutoBlitterChoose>(fDst, *fMatrix, localPaint)->get();
Florin Malita00dca8c2017-01-27 12:05:27 -05002037 if (blitterPtr->isNullBlitter()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038 continue;
2039 }
2040 }
2041 }
bsalomon49f085d2014-09-05 13:34:00 -07002042 if (colors) {
reed8a21c9f2016-03-08 18:50:00 -08002043 triShader->bindSetupData(&verticesSetup);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 }
reed@google.com045e62d2011-10-24 12:19:46 +00002045
2046 SkPoint tmp[] = {
2047 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
2048 };
Florin Malita00dca8c2017-01-27 12:05:27 -05002049 SkScan::FillTriangle(tmp, *fRC, blitterPtr);
2050 triShader->bindSetupData(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002052 } else {
commit-bot@chromium.org559a8832014-05-30 10:08:22 +00002053 // no colors[] and no texture, stroke hairlines with paint's color.
reed5dc6b7d2015-04-14 10:40:44 -07002054 SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
reed@google.com045e62d2011-10-24 12:19:46 +00002055 const SkRasterClip& clip = *fRC;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056 while (vertProc(&state)) {
reed5dc6b7d2015-04-14 10:40:44 -07002057 SkPoint array[] = {
2058 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
2059 };
2060 hairProc(array, 4, clip, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 }
2062 }
2063}
2064
reed@google.com0a0a2362011-03-23 13:51:55 +00002065///////////////////////////////////////////////////////////////////////////////
2066///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067
2068#ifdef SK_DEBUG
2069
reed@android.comf2b98d62010-12-20 18:26:13 +00002070void SkDraw::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002071 SkASSERT(fMatrix != nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -07002072 SkASSERT(fRC != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073
reed@google.com045e62d2011-10-24 12:19:46 +00002074 const SkIRect& cr = fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 SkIRect br;
2076
reed41e010c2015-06-09 12:16:53 -07002077 br.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078 SkASSERT(cr.isEmpty() || br.contains(cr));
2079}
2080
2081#endif
2082
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083////////////////////////////////////////////////////////////////////////////////////////////////
2084
2085#include "SkPath.h"
2086#include "SkDraw.h"
2087#include "SkRegion.h"
2088#include "SkBlitter.h"
2089
2090static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
reedb07a94f2014-11-19 05:03:18 -08002091 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 SkIRect* bounds) {
2093 if (devPath.isEmpty()) {
2094 return false;
2095 }
2096
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097 // init our bounds from the path
reed11fa2242015-03-13 06:08:28 -07002098 *bounds = devPath.getBounds().makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
reed@google.coma76de3d2011-01-13 18:30:42 +00002099
tomhudson@google.com6db75fc2012-03-23 14:46:48 +00002100 SkIPoint margin = SkIPoint::Make(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 if (filter) {
2102 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00002103
bungeman@google.com5af16f82011-09-02 15:06:44 +00002104 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00002105
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106 srcM.fBounds = *bounds;
2107 srcM.fFormat = SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
2109 return false;
2110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 }
2112
bungeman@google.com5af16f82011-09-02 15:06:44 +00002113 // (possibly) trim the bounds to reflect the clip
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 // (plus whatever slop the filter needs)
bungeman@google.com5af16f82011-09-02 15:06:44 +00002115 if (clipBounds) {
reed@android.com35555912009-03-16 18:46:55 +00002116 // Ugh. Guard against gigantic margins from wacky filters. Without this
2117 // check we can request arbitrary amounts of slop beyond our visible
2118 // clip, and bring down the renderer (at least on finite RAM machines
2119 // like handsets, etc.). Need to balance this invented value between
2120 // quality of large filters like blurs, and the corresponding memory
2121 // requests.
2122 static const int MAX_MARGIN = 128;
reed11fa2242015-03-13 06:08:28 -07002123 if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
2124 SkMin32(margin.fY, MAX_MARGIN)))) {
bungeman@google.com5af16f82011-09-02 15:06:44 +00002125 return false;
2126 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127 }
2128
2129 return true;
2130}
2131
bsalomon055e1922016-05-06 07:22:58 -07002132static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
2133 SkStrokeRec::InitStyle style) {
reed41e010c2015-06-09 12:16:53 -07002134 SkDraw draw;
2135 if (!draw.fDst.reset(mask)) {
2136 return;
2137 }
2138
reed@google.com045e62d2011-10-24 12:19:46 +00002139 SkRasterClip clip;
2140 SkMatrix matrix;
2141 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142
reed@google.com045e62d2011-10-24 12:19:46 +00002143 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002144 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
2145 -SkIntToScalar(mask.fBounds.fTop));
2146
reed@google.com045e62d2011-10-24 12:19:46 +00002147 draw.fRC = &clip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148 draw.fMatrix = &matrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 paint.setAntiAlias(true);
bsalomon055e1922016-05-06 07:22:58 -07002150 switch (style) {
2151 case SkStrokeRec::kHairline_InitStyle:
2152 SkASSERT(!paint.getStrokeWidth());
2153 paint.setStyle(SkPaint::kStroke_Style);
2154 break;
2155 case SkStrokeRec::kFill_InitStyle:
2156 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
2157 break;
2158
2159 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 draw.drawPath(devPath, paint);
2161}
2162
2163bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +00002164 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002165 SkMask* mask, SkMask::CreateMode mode,
bsalomon055e1922016-05-06 07:22:58 -07002166 SkStrokeRec::InitStyle style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 if (SkMask::kJustRenderImage_CreateMode != mode) {
2168 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
2169 return false;
2170 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002171
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
2173 mask->fFormat = SkMask::kA8_Format;
2174 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00002175 size_t size = mask->computeImageSize();
2176 if (0 == size) {
2177 // we're too big to allocate the mask, abort
2178 return false;
2179 }
2180 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 memset(mask->fImage, 0, mask->computeImageSize());
2182 }
2183
2184 if (SkMask::kJustComputeBounds_CreateMode != mode) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002185 draw_into_mask(*mask, devPath, style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002187
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 return true;
2189}