blob: d11bcd2f9fa5e5035b650214395e48fc25f47925 [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"
Mike Reed986480a2017-01-13 22:43:16 +000016#include "SkDevice.h"
reed@google.com1c028bd2013-08-28 15:23:19 +000017#include "SkDeviceLooper.h"
herbe5911c92015-11-09 13:15:21 -080018#include "SkFindAndPlaceGlyph.h"
bungeman@google.com2211b622012-01-13 15:02:58 +000019#include "SkFixed.h"
Florin Malita00dca8c2017-01-27 12:05:27 -050020#include "SkLocalMatrixShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkMaskFilter.h"
herbf553e4e2015-11-09 08:51:56 -080022#include "SkMatrix.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkPaint.h"
24#include "SkPathEffect.h"
reed@google.com045e62d2011-10-24 12:19:46 +000025#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkRasterizer.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000027#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkScan.h"
29#include "SkShader.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +000030#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031#include "SkStroke.h"
halcanary435657f2015-09-15 12:53:07 -070032#include "SkStrokeRec.h"
herbf553e4e2015-11-09 08:51:56 -080033#include "SkTemplates.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070034#include "SkTextMapStateProc.h"
reed@google.com32e5d972011-07-27 18:25:57 +000035#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000036#include "SkUtils.h"
commit-bot@chromium.org559a8832014-05-30 10:08:22 +000037#include "SkVertState.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
reed@android.com8a1c16f2008-12-17 15:59:43 +000039#include "SkBitmapProcShader.h"
40#include "SkDrawProcs.h"
reed@google.comae573582013-01-03 15:22:40 +000041#include "SkMatrixUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
43//#define TRACE_BITMAP_DRAWS
44
herbd4c24f62015-12-07 12:12:29 -080045// Helper function to fix code gen bug on ARM64.
46// See SkFindAndPlaceGlyph.h for more details.
47void FixGCC49Arm64Bug(int v) { }
reed@android.com8a1c16f2008-12-17 15:59:43 +000048
reed@google.comfd4236e2011-07-25 21:16:22 +000049/** Helper for allocating small blitters on the stack.
50 */
reed@google.com40c2ba22011-07-25 19:41:22 +000051class SkAutoBlitterChoose : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000052public:
reed@google.com1d6ee0b2011-07-05 17:44:56 +000053 SkAutoBlitterChoose() {
halcanary96fcdcc2015-08-27 07:41:13 -070054 fBlitter = nullptr;
reed@google.com1d6ee0b2011-07-05 17:44:56 +000055 }
reed41e010c2015-06-09 12:16:53 -070056 SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix,
reed@google.com126f7f52013-11-07 16:06:53 +000057 const SkPaint& paint, bool drawCoverage = false) {
Herb Derby83e939b2017-02-07 14:25:11 -050058 fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000059 }
herbe59124e2015-11-18 10:54:39 -080060
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 SkBlitter* operator->() { return fBlitter; }
62 SkBlitter* get() const { return fBlitter; }
63
reed41e010c2015-06-09 12:16:53 -070064 void choose(const SkPixmap& dst, const SkMatrix& matrix,
krajcevski53f09592014-08-06 11:12:14 -070065 const SkPaint& paint, bool drawCoverage = false) {
reed@google.com1d6ee0b2011-07-05 17:44:56 +000066 SkASSERT(!fBlitter);
Herb Derby83e939b2017-02-07 14:25:11 -050067 fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
reed@google.com1d6ee0b2011-07-05 17:44:56 +000068 }
69
reed@android.com8a1c16f2008-12-17 15:59:43 +000070private:
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000071 // Owned by fAllocator, which will handle the delete.
72 SkBlitter* fBlitter;
73 SkTBlitterAllocator fAllocator;
Herb Derby83e939b2017-02-07 14:25:11 -050074
75 // FIXME - pick a good inline and number.
76 SkArenaAlloc fAlloc{1024};
reed@android.com8a1c16f2008-12-17 15:59:43 +000077};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000078#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose)
reed@android.com8a1c16f2008-12-17 15:59:43 +000079
reed@google.com40c2ba22011-07-25 19:41:22 +000080/**
81 * Since we are providing the storage for the shader (to avoid the perf cost
82 * of calling new) we insist that in our destructor we can account for all
83 * owners of the shader.
84 */
85class SkAutoBitmapShaderInstall : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086public:
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +000087 SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint,
halcanary96fcdcc2015-08-27 07:41:13 -070088 const SkMatrix* localMatrix = nullptr)
reed@google.com40c2ba22011-07-25 19:41:22 +000089 : fPaint(paint) /* makes a copy of the paint */ {
Herb Derby83e939b2017-02-07 14:25:11 -050090 // TODO(herb): Move this over to SkArenaAlloc when arena alloc has a
91 // facility to return sk_sps.
reed8a21c9f2016-03-08 18:50:00 -080092 fPaint.setShader(SkMakeBitmapShader(src, SkShader::kClamp_TileMode,
reed1ec04d92016-08-05 12:07:41 -070093 SkShader::kClamp_TileMode, localMatrix,
94 kNever_SkCopyPixelsMode,
95 &fAllocator));
reed@google.com40c2ba22011-07-25 19:41:22 +000096 // we deliberately left the shader with an owner-count of 2
reed8a21c9f2016-03-08 18:50:00 -080097 fPaint.getShader()->ref();
reed@google.com40c2ba22011-07-25 19:41:22 +000098 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 }
reed@google.com82065d62011-02-07 15:30:46 +0000100
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 ~SkAutoBitmapShaderInstall() {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000102 // since fAllocator will destroy shader, we insist that owners == 2
103 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104
halcanary96fcdcc2015-08-27 07:41:13 -0700105 fPaint.setShader(nullptr); // unref the shader by 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 }
reed@google.com82065d62011-02-07 15:30:46 +0000108
reed@google.com40c2ba22011-07-25 19:41:22 +0000109 // return the new paint that has the shader applied
110 const SkPaint& paintWithShader() const { return fPaint; }
111
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112private:
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000113 // copy of caller's paint (which we then modify)
114 SkPaint fPaint;
115 // Stores the shader.
116 SkTBlitterAllocator fAllocator;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000118#define SkAutoBitmapShaderInstall(...) SK_REQUIRE_LOCAL_VAR(SkAutoBitmapShaderInstall)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120///////////////////////////////////////////////////////////////////////////////
121
reed@android.comf2b98d62010-12-20 18:26:13 +0000122SkDraw::SkDraw() {
123 sk_bzero(this, sizeof(*this));
124}
125
reed@google.com4bbdeac2013-01-24 21:03:11 +0000126bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
127 if (fRC->isEmpty()) {
128 return false;
129 }
130
131 SkMatrix inverse;
132 if (!fMatrix->invert(&inverse)) {
133 return false;
134 }
135
136 SkIRect devBounds = fRC->getBounds();
137 // outset to have slop for antialasing and hairlines
138 devBounds.outset(1, 1);
139 inverse.mapRect(localBounds, SkRect::Make(devBounds));
140 return true;
141}
142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143///////////////////////////////////////////////////////////////////////////////
144
145typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
146
147static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +0000148 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149}
150
151static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
152
153static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000154 sk_memset32((uint32_t*)pixels, data, SkToInt(bytes >> 2));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155}
156
157static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000158 sk_memset16((uint16_t*)pixels, data, SkToInt(bytes >> 1));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159}
160
161static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
162 memset(pixels, data, bytes);
163}
164
reed41e010c2015-06-09 12:16:53 -0700165static BitmapXferProc ChooseBitmapXferProc(const SkPixmap& dst, const SkPaint& paint,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 uint32_t* data) {
167 // todo: we can apply colorfilter up front if no shader, so we wouldn't
168 // need to abort this fastpath
169 if (paint.getShader() || paint.getColorFilter()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700170 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 }
172
reed374772b2016-10-05 17:33:02 -0700173 SkBlendMode mode = paint.getBlendMode();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 // collaps modes based on color...
reed374772b2016-10-05 17:33:02 -0700177 if (SkBlendMode::kSrcOver == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 unsigned alpha = SkColorGetA(color);
179 if (0 == alpha) {
reed374772b2016-10-05 17:33:02 -0700180 mode = SkBlendMode::kDst;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 } else if (0xFF == alpha) {
reed374772b2016-10-05 17:33:02 -0700182 mode = SkBlendMode::kSrc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 }
184 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 switch (mode) {
reed374772b2016-10-05 17:33:02 -0700187 case SkBlendMode::kClear:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188// SkDebugf("--- D_Clear_BitmapXferProc\n");
189 return D_Clear_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700190 case SkBlendMode::kDst:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191// SkDebugf("--- D_Dst_BitmapXferProc\n");
192 return D_Dst_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700193 case SkBlendMode::kSrc: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000195 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 */
197 SkPMColor pmc = SkPreMultiplyColor(color);
reed41e010c2015-06-09 12:16:53 -0700198 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000199 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 if (data) {
201 *data = pmc;
202 }
203// SkDebugf("--- D32_Src_BitmapXferProc\n");
204 return D32_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000205 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 if (data) {
207 *data = SkPixel32ToPixel16(pmc);
208 }
209// SkDebugf("--- D16_Src_BitmapXferProc\n");
210 return D16_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000211 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 if (data) {
213 *data = SkGetPackedA32(pmc);
214 }
215// SkDebugf("--- DA8_Src_BitmapXferProc\n");
216 return DA8_Src_BitmapXferProc;
217 default:
218 break;
219 }
220 break;
221 }
222 default:
223 break;
224 }
halcanary96fcdcc2015-08-27 07:41:13 -0700225 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226}
227
reed41e010c2015-06-09 12:16:53 -0700228static void CallBitmapXferProc(const SkPixmap& dst, const SkIRect& rect, BitmapXferProc proc,
229 uint32_t procData) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 int shiftPerPixel;
reed41e010c2015-06-09 12:16:53 -0700231 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000232 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 shiftPerPixel = 2;
234 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000235 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 shiftPerPixel = 1;
237 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000238 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 shiftPerPixel = 0;
240 break;
241 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000242 SkDEBUGFAIL("Can't use xferproc on this config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 return;
244 }
245
reed41e010c2015-06-09 12:16:53 -0700246 uint8_t* pixels = (uint8_t*)dst.writable_addr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 SkASSERT(pixels);
reed41e010c2015-06-09 12:16:53 -0700248 const size_t rowBytes = dst.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 const int widthBytes = rect.width() << shiftPerPixel;
250
251 // skip down to the first scanline and X position
252 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
253 for (int scans = rect.height() - 1; scans >= 0; --scans) {
254 proc(pixels, widthBytes, procData);
255 pixels += rowBytes;
256 }
257}
258
259void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000260 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261
reed@google.com045e62d2011-10-24 12:19:46 +0000262 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 return;
264 }
265
266 SkIRect devRect;
reed41e010c2015-06-09 12:16:53 -0700267 devRect.set(0, 0, fDst.width(), fDst.height());
reed@google.coma76de3d2011-01-13 18:30:42 +0000268
reed@google.com045e62d2011-10-24 12:19:46 +0000269 if (fRC->isBW()) {
270 /* If we don't have a shader (i.e. we're just a solid color) we may
271 be faster to operate directly on the device bitmap, rather than invoking
272 a blitter. Esp. true for xfermodes, which require a colorshader to be
273 present, which is just redundant work. Since we're drawing everywhere
274 in the clip, we don't have to worry about antialiasing.
275 */
276 uint32_t procData = 0; // to avoid the warning
reed41e010c2015-06-09 12:16:53 -0700277 BitmapXferProc proc = ChooseBitmapXferProc(fDst, paint, &procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000278 if (proc) {
279 if (D_Dst_BitmapXferProc == proc) { // nothing to do
280 return;
281 }
282
283 SkRegion::Iterator iter(fRC->bwRgn());
284 while (!iter.done()) {
reed41e010c2015-06-09 12:16:53 -0700285 CallBitmapXferProc(fDst, iter.rect(), proc, procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000286 iter.next();
287 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 return;
289 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 }
reed@google.com045e62d2011-10-24 12:19:46 +0000291
292 // normal case: use a blitter
reed41e010c2015-06-09 12:16:53 -0700293 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000294 SkScan::FillIRect(devRect, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295}
296
297///////////////////////////////////////////////////////////////////////////////
298
299struct PtProcRec {
300 SkCanvas::PointMode fMode;
301 const SkPaint* fPaint;
302 const SkRegion* fClip;
reed@google.com045e62d2011-10-24 12:19:46 +0000303 const SkRasterClip* fRC;
reed@google.coma76de3d2011-01-13 18:30:42 +0000304
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 // computed values
306 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000307
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
309 SkBlitter*);
310
311 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000312 const SkRasterClip*);
313 Proc chooseProc(SkBlitter** blitter);
314
315private:
316 SkAAClipBlitterWrapper fWrapper;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317};
318
319static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
320 int count, SkBlitter* blitter) {
321 SkASSERT(rec.fClip->isRect());
322 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000325 int x = SkScalarFloorToInt(devPts[i].fX);
326 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 if (r.contains(x, y)) {
328 blitter->blitH(x, y, 1);
329 }
330 }
331}
332
333static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
334 const SkPoint devPts[], int count,
335 SkBlitter* blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +0000336 SkASSERT(rec.fRC->isRect());
337 const SkIRect& r = rec.fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700339 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
340 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000341
reed41e010c2015-06-09 12:16:53 -0700342 uint16_t* addr = dst->writable_addr16(0, 0);
343 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000344
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000346 int x = SkScalarFloorToInt(devPts[i].fX);
347 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 if (r.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
350 }
351 }
352}
353
reed@google.com2d47a212012-11-29 21:01:00 +0000354static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
355 const SkPoint devPts[], int count,
356 SkBlitter* blitter) {
357 SkASSERT(rec.fRC->isRect());
358 const SkIRect& r = rec.fRC->getBounds();
359 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700360 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
361 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000362
reed41e010c2015-06-09 12:16:53 -0700363 SkPMColor* addr = dst->writable_addr32(0, 0);
364 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000365
reed@google.com2d47a212012-11-29 21:01:00 +0000366 for (int i = 0; i < count; i++) {
367 int x = SkScalarFloorToInt(devPts[i].fX);
368 int y = SkScalarFloorToInt(devPts[i].fY);
369 if (r.contains(x, y)) {
370 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
371 }
372 }
373}
374
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
376 int count, SkBlitter* blitter) {
377 for (int i = 0; i < count; i++) {
reed@google.come1ca7052013-12-17 19:22:07 +0000378 int x = SkScalarFloorToInt(devPts[i].fX);
379 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 if (rec.fClip->contains(x, y)) {
381 blitter->blitH(x, y, 1);
382 }
383 }
384}
385
386static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
387 int count, SkBlitter* blitter) {
388 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700389 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 }
391}
392
393static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
394 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700395 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396}
397
398// aa versions
399
400static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
401 int count, SkBlitter* blitter) {
402 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700403 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404 }
405}
406
407static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
408 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700409 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410}
411
412// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
413
414static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
415 int count, SkBlitter* blitter) {
416 const SkFixed radius = rec.fRadius;
417 for (int i = 0; i < count; i++) {
418 SkFixed x = SkScalarToFixed(devPts[i].fX);
419 SkFixed y = SkScalarToFixed(devPts[i].fY);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000420
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 SkXRect r;
422 r.fLeft = x - radius;
423 r.fTop = y - radius;
424 r.fRight = x + radius;
425 r.fBottom = y + radius;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000426
reed@google.com045e62d2011-10-24 12:19:46 +0000427 SkScan::FillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 }
429}
430
431static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
432 int count, SkBlitter* blitter) {
433 const SkFixed radius = rec.fRadius;
434 for (int i = 0; i < count; i++) {
435 SkFixed x = SkScalarToFixed(devPts[i].fX);
436 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 SkXRect r;
439 r.fLeft = x - radius;
440 r.fTop = y - radius;
441 r.fRight = x + radius;
442 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000443
reed@google.com045e62d2011-10-24 12:19:46 +0000444 SkScan::AntiFillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 }
446}
447
reed@android.comb4f404a2009-07-10 17:02:17 +0000448// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
reed@google.com045e62d2011-10-24 12:19:46 +0000450 const SkMatrix* matrix, const SkRasterClip* rc) {
ochang3ece53e2015-05-21 15:44:53 -0700451 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
452 return false;
453 }
454
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 if (paint.getPathEffect()) {
456 return false;
457 }
458 SkScalar width = paint.getStrokeWidth();
459 if (0 == width) {
460 fMode = mode;
461 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700462 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000463 fRC = rc;
reed@google.com2d47a212012-11-29 21:01:00 +0000464 fRadius = SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 return true;
466 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000467 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
robertphillips9f2251c2014-11-04 13:33:50 -0800468 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
470 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
471 if (SkScalarNearlyZero(sx - sy)) {
472 if (sx < 0) {
473 sx = -sx;
474 }
475
476 fMode = mode;
477 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700478 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000479 fRC = rc;
Mike Reeda99b6ce2017-02-04 11:04:26 -0500480 fRadius = SkScalarToFixed(width * sx) >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 return true;
482 }
483 }
484 return false;
485}
486
reed@google.com045e62d2011-10-24 12:19:46 +0000487PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700488 Proc proc = nullptr;
reed@google.coma76de3d2011-01-13 18:30:42 +0000489
reed@google.com045e62d2011-10-24 12:19:46 +0000490 SkBlitter* blitter = *blitterPtr;
491 if (fRC->isBW()) {
492 fClip = &fRC->bwRgn();
493 } else {
494 fWrapper.init(*fRC, blitter);
495 fClip = &fWrapper.getRgn();
496 blitter = fWrapper.getBlitter();
497 *blitterPtr = blitter;
498 }
499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 // for our arrays
501 SkASSERT(0 == SkCanvas::kPoints_PointMode);
502 SkASSERT(1 == SkCanvas::kLines_PointMode);
503 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
504 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
505
reed@google.com2d47a212012-11-29 21:01:00 +0000506 if (fPaint->isAntiAlias()) {
507 if (0 == fPaint->getStrokeWidth()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 static const Proc gAAProcs[] = {
509 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
510 };
511 proc = gAAProcs[fMode];
reed@google.com2d47a212012-11-29 21:01:00 +0000512 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
513 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
514 proc = aa_square_proc;
515 }
516 } else { // BW
517 if (fRadius <= SK_FixedHalf) { // small radii and hairline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
519 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700520 const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
reed@google.com900ecf22014-02-20 20:55:37 +0000521 if (bm && kRGB_565_SkColorType == bm->colorType()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 proc = bw_pt_rect_16_hair_proc;
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000523 } else if (bm && kN32_SkColorType == bm->colorType()) {
reed@google.com2d47a212012-11-29 21:01:00 +0000524 proc = bw_pt_rect_32_hair_proc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 } else {
526 proc = bw_pt_rect_hair_proc;
527 }
528 } else {
529 static Proc gBWProcs[] = {
530 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
531 };
532 proc = gBWProcs[fMode];
533 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 } else {
535 proc = bw_square_proc;
536 }
537 }
538 return proc;
539}
540
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541// each of these costs 8-bytes of stack space, so don't make it too large
542// must be even for lines/polygon to work
543#define MAX_DEV_PTS 32
544
545void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000546 const SkPoint pts[], const SkPaint& paint,
547 bool forceUseDevice) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 // if we're in lines mode, force count to be even
549 if (SkCanvas::kLines_PointMode == mode) {
550 count &= ~(size_t)1;
551 }
552
553 if ((long)count <= 0) {
554 return;
555 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000556
halcanary96fcdcc2015-08-27 07:41:13 -0700557 SkASSERT(pts != nullptr);
reed@android.comf2b98d62010-12-20 18:26:13 +0000558 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000561 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 return;
563 }
564
565 PtProcRec rec;
reed@google.com045e62d2011-10-24 12:19:46 +0000566 if (!forceUseDevice && rec.init(mode, paint, fMatrix, fRC)) {
reed41e010c2015-06-09 12:16:53 -0700567 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568
569 SkPoint devPts[MAX_DEV_PTS];
570 const SkMatrix* matrix = fMatrix;
571 SkBlitter* bltr = blitter.get();
reed@google.com045e62d2011-10-24 12:19:46 +0000572 PtProcRec::Proc proc = rec.chooseProc(&bltr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 // we have to back up subsequent passes if we're in polygon mode
574 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 do {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000577 int n = SkToInt(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 if (n > MAX_DEV_PTS) {
579 n = MAX_DEV_PTS;
580 }
581 matrix->mapPoints(devPts, pts, n);
582 proc(rec, devPts, n, bltr);
583 pts += n - backup;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000584 SkASSERT(SkToInt(count) >= n);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 count -= n;
586 if (count > 0) {
587 count += backup;
588 }
589 } while (count != 0);
590 } else {
591 switch (mode) {
592 case SkCanvas::kPoints_PointMode: {
593 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000594 SkPaint newPaint(paint);
595 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596
reed@google.com40c2ba22011-07-25 19:41:22 +0000597 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000599
reed@google.com40c2ba22011-07-25 19:41:22 +0000600 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 SkPath path;
602 SkMatrix preMatrix;
mtklein7ef849d2014-11-24 09:11:45 -0800603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 path.addCircle(0, 0, radius);
605 for (size_t i = 0; i < count; i++) {
606 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
607 // pass true for the last point, since we can modify
608 // then path then
jvanverthb3eb6872014-10-24 07:12:51 -0700609 path.setIsVolatile((count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000610 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000611 fDevice->drawPath(*this, path, newPaint, &preMatrix,
reed@android.comf2b98d62010-12-20 18:26:13 +0000612 (count-1) == i);
613 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000614 this->drawPath(path, newPaint, &preMatrix,
615 (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000616 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 }
618 } else {
619 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 for (size_t i = 0; i < count; i++) {
622 r.fLeft = pts[i].fX - radius;
623 r.fTop = pts[i].fY - radius;
624 r.fRight = r.fLeft + width;
625 r.fBottom = r.fTop + width;
reed@android.comf2b98d62010-12-20 18:26:13 +0000626 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000627 fDevice->drawRect(*this, r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000628 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000629 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000630 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 }
632 }
633 break;
634 }
635 case SkCanvas::kLines_PointMode:
bsalomon49f085d2014-09-05 13:34:00 -0700636 if (2 == count && paint.getPathEffect()) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000637 // most likely a dashed line - see if it is one of the ones
638 // we can accelerate
639 SkStrokeRec rec(paint);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000640 SkPathEffect::PointData pointData;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000641
642 SkPath path;
643 path.moveTo(pts[0]);
644 path.lineTo(pts[1]);
645
reed@google.com4bbdeac2013-01-24 21:03:11 +0000646 SkRect cullRect = SkRect::Make(fRC->getBounds());
skia.committer@gmail.com4024f322013-01-25 07:06:46 +0000647
reed@google.com4bbdeac2013-01-24 21:03:11 +0000648 if (paint.getPathEffect()->asPoints(&pointData, path, rec,
649 *fMatrix, &cullRect)) {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000650 // 'asPoints' managed to find some fast path
651
robertphillips@google.com629ab542012-11-28 17:18:11 +0000652 SkPaint newP(paint);
halcanary96fcdcc2015-08-27 07:41:13 -0700653 newP.setPathEffect(nullptr);
robertphillips@google.com935ad022012-12-05 19:07:21 +0000654 newP.setStyle(SkPaint::kFill_Style);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000655
robertphillips@google.com6d875572012-12-17 18:56:29 +0000656 if (!pointData.fFirst.isEmpty()) {
657 if (fDevice) {
658 fDevice->drawPath(*this, pointData.fFirst, newP);
659 } else {
660 this->drawPath(pointData.fFirst, newP);
661 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000662 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000663
664 if (!pointData.fLast.isEmpty()) {
665 if (fDevice) {
666 fDevice->drawPath(*this, pointData.fLast, newP);
667 } else {
668 this->drawPath(pointData.fLast, newP);
669 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000670 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000671
672 if (pointData.fSize.fX == pointData.fSize.fY) {
673 // The rest of the dashed line can just be drawn as points
674 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
675
676 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
677 newP.setStrokeCap(SkPaint::kRound_Cap);
678 } else {
679 newP.setStrokeCap(SkPaint::kButt_Cap);
680 }
681
682 if (fDevice) {
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000683 fDevice->drawPoints(*this,
robertphillips@google.com6d875572012-12-17 18:56:29 +0000684 SkCanvas::kPoints_PointMode,
685 pointData.fNumPoints,
686 pointData.fPoints,
687 newP);
688 } else {
689 this->drawPoints(SkCanvas::kPoints_PointMode,
690 pointData.fNumPoints,
691 pointData.fPoints,
692 newP,
693 forceUseDevice);
694 }
695 break;
696 } else {
697 // The rest of the dashed line must be drawn as rects
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000698 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
robertphillips@google.com6d875572012-12-17 18:56:29 +0000699 pointData.fFlags));
700
701 SkRect r;
702
703 for (int i = 0; i < pointData.fNumPoints; ++i) {
704 r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
705 pointData.fPoints[i].fY - pointData.fSize.fY,
706 pointData.fPoints[i].fX + pointData.fSize.fX,
707 pointData.fPoints[i].fY + pointData.fSize.fY);
708 if (fDevice) {
709 fDevice->drawRect(*this, r, newP);
710 } else {
711 this->drawRect(r, newP);
712 }
713 }
714 }
715
robertphillips@google.com629ab542012-11-28 17:18:11 +0000716 break;
717 }
718 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000719 // couldn't take fast path so fall through!
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 case SkCanvas::kPolygon_PointMode: {
721 count -= 1;
722 SkPath path;
723 SkPaint p(paint);
724 p.setStyle(SkPaint::kStroke_Style);
725 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
jvanverthb3eb6872014-10-24 07:12:51 -0700726 path.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 for (size_t i = 0; i < count; i += inc) {
728 path.moveTo(pts[i]);
729 path.lineTo(pts[i+1]);
reed@android.comf2b98d62010-12-20 18:26:13 +0000730 if (fDevice) {
halcanary96fcdcc2015-08-27 07:41:13 -0700731 fDevice->drawPath(*this, path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000732 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700733 this->drawPath(path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 path.rewind();
736 }
737 break;
738 }
739 }
740 }
741}
742
fmalita1a178ca2015-01-15 06:01:23 -0800743static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
744 SkASSERT(matrix.rectStaysRect());
745 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
746
747 SkVector size;
748 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
749 matrix.mapVectors(&size, &pt, 1);
750 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
751}
752
reed@google.com761fb622011-04-04 18:58:05 +0000753static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
754 SkPoint* strokeSize) {
755 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
756 paint.getStrokeMiter() < SK_ScalarSqrt2) {
757 return false;
758 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000759
fmalita1a178ca2015-01-15 06:01:23 -0800760 *strokeSize = compute_stroke_size(paint, matrix);
reed@google.com761fb622011-04-04 18:58:05 +0000761 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000762}
763
reed@google.com62ab7ad2011-04-05 14:08:25 +0000764SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
765 const SkMatrix& matrix,
766 SkPoint* strokeSize) {
767 RectType rtype;
768 const SkScalar width = paint.getStrokeWidth();
769 const bool zeroWidth = (0 == width);
770 SkPaint::Style style = paint.getStyle();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000771
reed@google.com62ab7ad2011-04-05 14:08:25 +0000772 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
773 style = SkPaint::kFill_Style;
774 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000775
reed@google.com62ab7ad2011-04-05 14:08:25 +0000776 if (paint.getPathEffect() || paint.getMaskFilter() ||
777 paint.getRasterizer() || !matrix.rectStaysRect() ||
778 SkPaint::kStrokeAndFill_Style == style) {
779 rtype = kPath_RectType;
780 } else if (SkPaint::kFill_Style == style) {
781 rtype = kFill_RectType;
782 } else if (zeroWidth) {
783 rtype = kHair_RectType;
784 } else if (easy_rect_join(paint, matrix, strokeSize)) {
785 rtype = kStroke_RectType;
786 } else {
787 rtype = kPath_RectType;
788 }
789 return rtype;
790}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000791
reed@google.com73244152012-05-11 14:35:23 +0000792static const SkPoint* rect_points(const SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000793 return SkTCast<const SkPoint*>(&r);
reed@google.com73244152012-05-11 14:35:23 +0000794}
795
796static SkPoint* rect_points(SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000797 return SkTCast<SkPoint*>(&r);
reed@google.com40c2ba22011-07-25 19:41:22 +0000798}
799
reed03939122014-12-15 13:42:51 -0800800void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
801 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000802 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803
804 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000805 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 return;
807 }
808
reed03939122014-12-15 13:42:51 -0800809 const SkMatrix* matrix;
810 SkMatrix combinedMatrixStorage;
811 if (paintMatrix) {
812 SkASSERT(postPaintRect);
813 combinedMatrixStorage.setConcat(*fMatrix, *paintMatrix);
814 matrix = &combinedMatrixStorage;
815 } else {
816 SkASSERT(!postPaintRect);
817 matrix = fMatrix;
818 }
819
reed@google.com761fb622011-04-04 18:58:05 +0000820 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000821 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000822
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000823 if (kPath_RectType == rtype) {
reed03939122014-12-15 13:42:51 -0800824 SkDraw draw(*this);
825 if (paintMatrix) {
826 draw.fMatrix = matrix;
827 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 SkPath tmp;
reed03939122014-12-15 13:42:51 -0800829 tmp.addRect(prePaintRect);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 tmp.setFillType(SkPath::kWinding_FillType);
halcanary96fcdcc2015-08-27 07:41:13 -0700831 draw.drawPath(tmp, paint, nullptr, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return;
833 }
834
reed03939122014-12-15 13:42:51 -0800835 SkRect devRect;
fmalita1a178ca2015-01-15 06:01:23 -0800836 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
837 // skip the paintMatrix when transforming the rect by the CTM
838 fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
reed@google.com73244152012-05-11 14:35:23 +0000839 devRect.sort();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 // look for the quick exit, before we build a blitter
fmalita1a178ca2015-01-15 06:01:23 -0800842 SkRect bbox = devRect;
reed@google.com1c028bd2013-08-28 15:23:19 +0000843 if (paint.getStyle() != SkPaint::kFill_Style) {
844 // extra space for hairlines
georgeb3eba472014-09-09 11:33:57 -0700845 if (paint.getStrokeWidth() == 0) {
fmalita1a178ca2015-01-15 06:01:23 -0800846 bbox.outset(1, 1);
georgeb3eba472014-09-09 11:33:57 -0700847 } else {
fmalita1a178ca2015-01-15 06:01:23 -0800848 // For kStroke_RectType, strokeSize is already computed.
849 const SkPoint& ssize = (kStroke_RectType == rtype)
850 ? strokeSize
851 : compute_stroke_size(paint, *fMatrix);
852 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
georgeb3eba472014-09-09 11:33:57 -0700853 }
reed@google.com1c028bd2013-08-28 15:23:19 +0000854 }
fmalita1a178ca2015-01-15 06:01:23 -0800855
856 SkIRect ir = bbox.roundOut();
reed@google.com1c028bd2013-08-28 15:23:19 +0000857 if (fRC->quickReject(ir)) {
858 return;
rmistry@google.come09d6f42013-08-27 18:53:41 +0000859 }
860
reed41e010c2015-06-09 12:16:53 -0700861 SkDeviceLooper looper(fDst, *fRC, ir, paint.isAntiAlias());
reed@google.com1c028bd2013-08-28 15:23:19 +0000862 while (looper.next()) {
863 SkRect localDevRect;
864 looper.mapRect(&localDevRect, devRect);
865 SkMatrix localMatrix;
reed03939122014-12-15 13:42:51 -0800866 looper.mapMatrix(&localMatrix, *matrix);
rmistry@google.come09d6f42013-08-27 18:53:41 +0000867
reed41e010c2015-06-09 12:16:53 -0700868 SkAutoBlitterChoose blitterStorage(looper.getPixmap(), localMatrix, paint);
reed@google.com1c028bd2013-08-28 15:23:19 +0000869 const SkRasterClip& clip = looper.getRC();
870 SkBlitter* blitter = blitterStorage.get();
871
872 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
873 // case we are also hairline (if we've gotten to here), which devolves to
874 // effectively just kFill
875 switch (rtype) {
876 case kFill_RectType:
877 if (paint.isAntiAlias()) {
878 SkScan::AntiFillRect(localDevRect, clip, blitter);
879 } else {
880 SkScan::FillRect(localDevRect, clip, blitter);
881 }
882 break;
883 case kStroke_RectType:
884 if (paint.isAntiAlias()) {
885 SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
886 } else {
887 SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
888 }
889 break;
890 case kHair_RectType:
891 if (paint.isAntiAlias()) {
892 SkScan::AntiHairRect(localDevRect, clip, blitter);
893 } else {
894 SkScan::HairRect(localDevRect, clip, blitter);
895 }
896 break;
897 default:
898 SkDEBUGFAIL("bad rtype");
899 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 }
901}
902
903void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
904 if (srcM.fBounds.isEmpty()) {
905 return;
906 }
907
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000908 const SkMask* mask = &srcM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000910 SkMask dstM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 if (paint.getMaskFilter() &&
robertphillipse80eb922015-12-17 11:33:12 -0800912 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 mask = &dstM;
914 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000915 SkAutoMaskFreeImage ami(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916
reed41e010c2015-06-09 12:16:53 -0700917 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000918 SkBlitter* blitter = blitterChooser.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919
reed@google.com045e62d2011-10-24 12:19:46 +0000920 SkAAClipBlitterWrapper wrapper;
921 const SkRegion* clipRgn;
922
923 if (fRC->isBW()) {
924 clipRgn = &fRC->bwRgn();
925 } else {
926 wrapper.init(*fRC, blitter);
927 clipRgn = &wrapper.getRgn();
928 blitter = wrapper.getBlitter();
929 }
930 blitter->blitMaskRegion(*mask, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931}
932
reed@android.comebdeeb82009-09-03 21:45:49 +0000933static SkScalar fast_len(const SkVector& vec) {
934 SkScalar x = SkScalarAbs(vec.fX);
935 SkScalar y = SkScalarAbs(vec.fY);
936 if (x < y) {
937 SkTSwap(x, y);
938 }
939 return x + SkScalarHalf(y);
940}
941
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000942bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
943 SkScalar* coverage) {
944 SkASSERT(strokeWidth > 0);
945 // We need to try to fake a thick-stroke with a modulated hairline.
reed@google.comecadf992011-09-19 19:18:18 +0000946
tomhudson@google.com8d430182011-06-06 19:11:19 +0000947 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000948 return false;
949 }
reed@google.comecadf992011-09-19 19:18:18 +0000950
reed@android.comebdeeb82009-09-03 21:45:49 +0000951 SkVector src[2], dst[2];
reed@google.comecadf992011-09-19 19:18:18 +0000952 src[0].set(strokeWidth, 0);
953 src[1].set(0, strokeWidth);
reed@android.comebdeeb82009-09-03 21:45:49 +0000954 matrix.mapVectors(dst, src, 2);
955 SkScalar len0 = fast_len(dst[0]);
956 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000957 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
bsalomon49f085d2014-09-05 13:34:00 -0700958 if (coverage) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000959 *coverage = SkScalarAve(len0, len1);
960 }
reed@android.comebdeeb82009-09-03 21:45:49 +0000961 return true;
962 }
963 return false;
964}
965
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000966void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
967 SkDEBUGCODE(this->validate());
968
969 if (fRC->isEmpty()) {
970 return;
971 }
972
973 {
974 // TODO: Investigate optimizing these options. They are in the same
975 // order as SkDraw::drawPath, which handles each case. It may be
976 // that there is no way to optimize for these using the SkRRect path.
977 SkScalar coverage;
978 if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
979 goto DRAW_PATH;
980 }
981
982 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
983 goto DRAW_PATH;
984 }
985
986 if (paint.getRasterizer()) {
987 goto DRAW_PATH;
988 }
989 }
990
991 if (paint.getMaskFilter()) {
992 // Transform the rrect into device space.
993 SkRRect devRRect;
994 if (rrect.transform(*fMatrix, &devRRect)) {
reed41e010c2015-06-09 12:16:53 -0700995 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
bsalomon055e1922016-05-06 07:22:58 -0700996 if (paint.getMaskFilter()->filterRRect(devRRect, *fMatrix, *fRC, blitter.get())) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000997 return; // filterRRect() called the blitter, so we're done
998 }
999 }
1000 }
1001
1002DRAW_PATH:
1003 // Now fall back to the default case of using a path.
1004 SkPath path;
1005 path.addRRect(rrect);
halcanary96fcdcc2015-08-27 07:41:13 -07001006 this->drawPath(path, paint, nullptr, true);
scroggo@google.coma8e33a92013-11-08 18:02:53 +00001007}
1008
caryclark1a7eb262016-01-21 07:07:02 -08001009SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
reed05d90442015-02-12 13:35:52 -08001010 if (!matrix.hasPerspective()) {
1011 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
1012 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
1013 if (SkScalarsAreFinite(sx, sy)) {
lsalzmana4b87042016-07-06 12:19:56 -07001014 SkScalar scale = SkTMax(sx, sy);
1015 if (scale > 0) {
1016 return scale;
1017 }
reed05d90442015-02-12 13:35:52 -08001018 }
1019 }
1020 return 1;
1021}
1022
reed82595b62016-05-09 17:48:46 -07001023void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
1024 SkBlitter* customBlitter, bool doFill) const {
reed01a2ff82016-07-18 13:22:55 -07001025 // Do a conservative quick-reject test, since a looper or other modifier may have moved us
1026 // out of range.
1027 if (!devPath.isInverseFillType()) {
1028 // If we're a H or V line, our bounds will be empty. So we bloat here just so we don't
1029 // appear empty to the intersects call. This also gives us slop in case we're antialiasing
1030 SkRect pathBounds = devPath.getBounds().makeOutset(1, 1);
1031
1032 if (paint.getMaskFilter()) {
1033 paint.getMaskFilter()->computeFastBounds(pathBounds, &pathBounds);
1034
1035 // Need to outset the path to work-around a bug in blurmaskfilter. When that is fixed
1036 // we can remove this hack. See skbug.com/5542
1037 pathBounds.outset(7, 7);
1038 }
1039
1040 // Now compare against the clip's bounds
1041 if (!SkRect::Make(fRC->getBounds()).intersects(pathBounds)) {
1042 return;
1043 }
1044 }
1045
reed82595b62016-05-09 17:48:46 -07001046 SkBlitter* blitter = nullptr;
1047 SkAutoBlitterChoose blitterStorage;
1048 if (nullptr == customBlitter) {
1049 blitterStorage.choose(fDst, *fMatrix, paint, drawCoverage);
1050 blitter = blitterStorage.get();
1051 } else {
1052 blitter = customBlitter;
1053 }
1054
1055 if (paint.getMaskFilter()) {
1056 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
1057 : SkStrokeRec::kHairline_InitStyle;
1058 if (paint.getMaskFilter()->filterPath(devPath, *fMatrix, *fRC, blitter, style)) {
1059 return; // filterPath() called the blitter, so we're done
1060 }
1061 }
1062
1063 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
1064 if (doFill) {
1065 if (paint.isAntiAlias()) {
1066 proc = SkScan::AntiFillPath;
1067 } else {
1068 proc = SkScan::FillPath;
1069 }
1070 } else { // hairline
1071 if (paint.isAntiAlias()) {
1072 switch (paint.getStrokeCap()) {
1073 case SkPaint::kButt_Cap:
1074 proc = SkScan::AntiHairPath;
1075 break;
1076 case SkPaint::kSquare_Cap:
1077 proc = SkScan::AntiHairSquarePath;
1078 break;
1079 case SkPaint::kRound_Cap:
1080 proc = SkScan::AntiHairRoundPath;
1081 break;
1082 default:
1083 proc SK_INIT_TO_AVOID_WARNING;
1084 SkDEBUGFAIL("unknown paint cap type");
1085 }
1086 } else {
1087 switch (paint.getStrokeCap()) {
1088 case SkPaint::kButt_Cap:
1089 proc = SkScan::HairPath;
1090 break;
1091 case SkPaint::kSquare_Cap:
1092 proc = SkScan::HairSquarePath;
1093 break;
1094 case SkPaint::kRound_Cap:
1095 proc = SkScan::HairRoundPath;
1096 break;
1097 default:
1098 proc SK_INIT_TO_AVOID_WARNING;
1099 SkDEBUGFAIL("unknown paint cap type");
1100 }
1101 }
1102 }
1103 proc(devPath, *fRC, blitter);
1104}
1105
reed@google.com32e5d972011-07-27 18:25:57 +00001106void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
reed@google.com126f7f52013-11-07 16:06:53 +00001107 const SkMatrix* prePathMatrix, bool pathIsMutable,
krajcevski53f09592014-08-06 11:12:14 -07001108 bool drawCoverage, SkBlitter* customBlitter) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001109 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
1111 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001112 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 return;
1114 }
1115
1116 SkPath* pathPtr = (SkPath*)&origSrcPath;
1117 bool doFill = true;
1118 SkPath tmpPath;
1119 SkMatrix tmpMatrix;
1120 const SkMatrix* matrix = fMatrix;
jvanverthb3eb6872014-10-24 07:12:51 -07001121 tmpPath.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122
1123 if (prePathMatrix) {
reed@google.com32e5d972011-07-27 18:25:57 +00001124 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
1125 origPaint.getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +00001127
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 if (!pathIsMutable) {
1129 result = &tmpPath;
1130 pathIsMutable = true;
1131 }
1132 pathPtr->transform(*prePathMatrix, result);
1133 pathPtr = result;
1134 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001135 tmpMatrix.setConcat(*matrix, *prePathMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 matrix = &tmpMatrix;
1137 }
1138 }
1139 // at this point we're done with prePathMatrix
1140 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +00001141
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001142 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
reed@google.coma76de3d2011-01-13 18:30:42 +00001143
reed@google.comecadf992011-09-19 19:18:18 +00001144 {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001145 SkScalar coverage;
1146 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
1147 if (SK_Scalar1 == coverage) {
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001148 paint.writable()->setStrokeWidth(0);
reed374772b2016-10-05 17:33:02 -07001149 } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001150 U8CPU newAlpha;
1151#if 0
1152 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
1153 origPaint.getAlpha()));
1154#else
1155 // this is the old technique, which we preserve for now so
1156 // we don't change previous results (testing)
1157 // the new way seems fine, its just (a tiny bit) different
Mike Reeda99b6ce2017-02-04 11:04:26 -05001158 int scale = (int)(coverage * 256);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001159 newAlpha = origPaint.getAlpha() * scale >> 8;
1160#endif
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001161 SkPaint* writablePaint = paint.writable();
1162 writablePaint->setStrokeWidth(0);
1163 writablePaint->setAlpha(newAlpha);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001164 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 }
1166 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001167
reed@google.com32e5d972011-07-27 18:25:57 +00001168 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
reed@google.com4bbdeac2013-01-24 21:03:11 +00001169 SkRect cullRect;
halcanary96fcdcc2015-08-27 07:41:13 -07001170 const SkRect* cullRectPtr = nullptr;
reed@google.com4bbdeac2013-01-24 21:03:11 +00001171 if (this->computeConservativeLocalClipBounds(&cullRect)) {
1172 cullRectPtr = &cullRect;
1173 }
reed05d90442015-02-12 13:35:52 -08001174 doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr,
caryclark1a7eb262016-01-21 07:07:02 -08001175 ComputeResScaleForStroking(*fMatrix));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 pathPtr = &tmpPath;
1177 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001178
reed@google.com32e5d972011-07-27 18:25:57 +00001179 if (paint->getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 SkMask mask;
reed@google.com32e5d972011-07-27 18:25:57 +00001181 if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
reed@google.com045e62d2011-10-24 12:19:46 +00001182 &fRC->getBounds(), paint->getMaskFilter(), &mask,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
reed@google.com32e5d972011-07-27 18:25:57 +00001184 this->drawDevMask(mask, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 SkMask::FreeImage(mask.fImage);
1186 }
1187 return;
1188 }
1189
1190 // avoid possibly allocating a new path in transform if we can
1191 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1192
1193 // transform the path into device space
1194 pathPtr->transform(*matrix, devPathPtr);
1195
reed82595b62016-05-09 17:48:46 -07001196 this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
reed82595b62016-05-09 17:48:46 -07001199void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {
reed@google.com900ecf22014-02-20 20:55:37 +00001200 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201
fmalitac7e211a2016-01-07 10:34:46 -08001202 if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) {
reed@google.come1ca7052013-12-17 19:22:07 +00001203 int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
1204 int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205
reed92fc2ae2015-05-22 08:06:21 -07001206 SkAutoPixmapUnlock result;
1207 if (!bitmap.requestLock(&result)) {
reed@google.coma641f3f2012-12-13 22:16:30 +00001208 return;
1209 }
reed92fc2ae2015-05-22 08:06:21 -07001210 const SkPixmap& pmap = result.pixmap();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 SkMask mask;
reed92fc2ae2015-05-22 08:06:21 -07001212 mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 mask.fFormat = SkMask::kA8_Format;
reed92fc2ae2015-05-22 08:06:21 -07001214 mask.fRowBytes = SkToU32(pmap.rowBytes());
1215 // fImage is typed as writable, but in this case it is used read-only
1216 mask.fImage = (uint8_t*)pmap.addr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001217
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218 this->drawDevMask(mask, paint);
1219 } else { // need to xform the bitmap first
1220 SkRect r;
1221 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001222
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 r.set(0, 0,
1224 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1225 fMatrix->mapRect(&r);
1226 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001227
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 // set the mask's bounds to the transformed bitmap-bounds,
1229 // clipped to the actual device
1230 {
1231 SkIRect devBounds;
reed41e010c2015-06-09 12:16:53 -07001232 devBounds.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 // need intersect(l, t, r, b) on irect
1234 if (!mask.fBounds.intersect(devBounds)) {
1235 return;
1236 }
1237 }
reed@android.com543ed932009-04-24 12:43:40 +00001238
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 mask.fFormat = SkMask::kA8_Format;
1240 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001241 size_t size = mask.computeImageSize();
1242 if (0 == size) {
1243 // the mask is too big to allocated, draw nothing
1244 return;
1245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246
1247 // allocate (and clear) our temp buffer to hold the transformed bitmap
scroggo565901d2015-12-10 10:44:13 -08001248 SkAutoTMalloc<uint8_t> storage(size);
1249 mask.fImage = storage.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001251
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1253 {
1254 SkBitmap device;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001255 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
1256 mask.fImage, mask.fRowBytes);
reed@google.coma76de3d2011-01-13 18:30:42 +00001257
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 SkCanvas c(device);
1259 // need the unclipped top/left for the translate
1260 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1261 -SkIntToScalar(mask.fBounds.fTop));
1262 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001263
1264 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001265 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001266 SkPaint tmpPaint;
1267 tmpPaint.setFlags(paint.getFlags());
brianosman66a96d02016-05-19 08:43:55 -07001268 tmpPaint.setFilterQuality(paint.getFilterQuality());
reed@google.com40c2ba22011-07-25 19:41:22 +00001269 SkAutoBitmapShaderInstall install(bitmap, tmpPaint);
reed@android.com3469c762009-02-24 19:03:20 +00001270 SkRect rr;
1271 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1272 SkIntToScalar(bitmap.height()));
reed@google.com40c2ba22011-07-25 19:41:22 +00001273 c.drawRect(rr, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 }
1275 this->drawDevMask(mask, paint);
1276 }
1277}
1278
reed@google.com045e62d2011-10-24 12:19:46 +00001279static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 const SkRect& srcR) {
1281 SkRect dstR;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 m.mapRect(&dstR, srcR);
reedb07a94f2014-11-19 05:03:18 -08001283 return c.quickReject(dstR.roundOut());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284}
1285
reed@google.com045e62d2011-10-24 12:19:46 +00001286static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 int width, int height) {
1288 SkRect r;
1289 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1290 return clipped_out(matrix, clip, r);
1291}
1292
reedc240e712015-05-23 12:26:41 -07001293static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
1294 return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
reed@google.com045e62d2011-10-24 12:19:46 +00001295}
1296
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed03939122014-12-15 13:42:51 -08001298 const SkRect* dstBounds, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001299 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300
1301 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001302 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001304 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 return;
1306 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001307
fmalitaf85d2a42016-10-03 13:46:58 -07001308 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1309 if (origPaint.getStyle() != SkPaint::kFill_Style) {
1310 paint.writable()->setStyle(SkPaint::kFill_Style);
1311 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001312
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 SkMatrix matrix;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001314 matrix.setConcat(*fMatrix, prematrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315
reed@google.com045e62d2011-10-24 12:19:46 +00001316 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 return;
1318 }
1319
fmalitac7e211a2016-01-07 10:34:46 -08001320 if (bitmap.colorType() != kAlpha_8_SkColorType
fmalitaf85d2a42016-10-03 13:46:58 -07001321 && SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001322 //
1323 // It is safe to call lock pixels now, since we know the matrix is
1324 // (more or less) identity.
1325 //
reedc240e712015-05-23 12:26:41 -07001326 SkAutoPixmapUnlock unlocker;
1327 if (!bitmap.requestLock(&unlocker)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001328 return;
1329 }
reedc240e712015-05-23 12:26:41 -07001330 const SkPixmap& pmap = unlocker.pixmap();
reed@google.come1ca7052013-12-17 19:22:07 +00001331 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1332 int iy = SkScalarRoundToInt(matrix.getTranslateY());
reedc240e712015-05-23 12:26:41 -07001333 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
Herb Derby57bfa022017-02-09 17:25:43 -05001334 char storage[kSkBlitterContextSize];
1335 SkArenaAlloc allocator{storage};
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001336 // blitter will be owned by the allocator.
fmalitaf85d2a42016-10-03 13:46:58 -07001337 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator);
reed@google.com045e62d2011-10-24 12:19:46 +00001338 if (blitter) {
reedc240e712015-05-23 12:26:41 -07001339 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
1340 *fRC, blitter);
reed@google.com045e62d2011-10-24 12:19:46 +00001341 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 }
reedc240e712015-05-23 12:26:41 -07001343 // if !blitter, then we fall-through to the slower case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001346
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 // now make a temp draw on the stack, and use it
1348 //
1349 SkDraw draw(*this);
1350 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001351
Matt Sarett70ac8a92016-11-03 10:55:08 -04001352 if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
fmalitaf85d2a42016-10-03 13:46:58 -07001353 draw.drawBitmapAsMask(bitmap, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 } else {
fmalitaf85d2a42016-10-03 13:46:58 -07001355 SkAutoBitmapShaderInstall install(bitmap, *paint);
reed03939122014-12-15 13:42:51 -08001356 const SkPaint& paintWithShader = install.paintWithShader();
1357 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1358 if (dstBounds) {
1359 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1360 } else {
1361 draw.drawRect(srcBounds, paintWithShader);
1362 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 }
1364}
1365
reedc240e712015-05-23 12:26:41 -07001366void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001367 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001368
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001370 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001372 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 return;
1374 }
1375
reedc240e712015-05-23 12:26:41 -07001376 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377
reed@google.com045e62d2011-10-24 12:19:46 +00001378 if (fRC->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 return; // nothing to draw
1380 }
1381
reed@google.com40c2ba22011-07-25 19:41:22 +00001382 SkPaint paint(origPaint);
1383 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384
reedc240e712015-05-23 12:26:41 -07001385 SkAutoPixmapUnlock unlocker;
1386 if (!bitmap.requestLock(&unlocker)) {
1387 return;
1388 }
1389 const SkPixmap& pmap = unlocker.pixmap();
1390
halcanary96fcdcc2015-08-27 07:41:13 -07001391 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001392 // blitter will be owned by the allocator.
Herb Derby57bfa022017-02-09 17:25:43 -05001393 char storage[kSkBlitterContextSize];
1394 SkArenaAlloc allocator{storage};
reed41e010c2015-06-09 12:16:53 -07001395 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 if (blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +00001397 SkScan::FillIRect(bounds, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 return;
1399 }
1400 }
1401
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 SkMatrix matrix;
1403 SkRect r;
1404
1405 // get a scalar version of our rect
1406 r.set(bounds);
1407
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001408 // create shader with offset
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 matrix.setTranslate(r.fLeft, r.fTop);
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001410 SkAutoBitmapShaderInstall install(bitmap, paint, &matrix);
1411 const SkPaint& shaderPaint = install.paintWithShader();
reed@google.coma76de3d2011-01-13 18:30:42 +00001412
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 SkDraw draw(*this);
1414 matrix.reset();
1415 draw.fMatrix = &matrix;
1416 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001417 // is this OK if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001418 draw.drawRect(r, shaderPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419}
1420
1421///////////////////////////////////////////////////////////////////////////////
1422
1423#include "SkScalerContext.h"
1424#include "SkGlyphCache.h"
reed@google.come6913762012-08-07 15:19:47 +00001425#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426#include "SkUtils.h"
1427
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001428bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
1429 // hairline glyphs are fast enough so we don't need to cache them
1430 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
1431 return true;
1432 }
1433
1434 // we don't cache perspective
1435 if (ctm.hasPerspective()) {
1436 return true;
1437 }
1438
1439 SkMatrix textM;
1440 return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM));
1441}
1442
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
1444 SkScalar x, SkScalar y,
1445 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001446 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447
djsollen@google.com166e6532012-03-20 14:24:38 +00001448 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449
1450 SkMatrix matrix;
1451 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1452 matrix.postTranslate(x, y);
1453
1454 const SkPath* iterPath;
1455 SkScalar xpos, prevXPos = 0;
1456
reed@google.com7b4531f2012-08-07 15:53:00 +00001457 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001459 if (iterPath) {
1460 const SkPaint& pnt = iter.getPaint();
1461 if (fDevice) {
1462 fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
1463 } else {
1464 this->drawPath(*iterPath, pnt, &matrix, false);
1465 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001466 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 prevXPos = xpos;
1468 }
1469}
1470
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471// disable warning : local variable used without having been initialized
bungemand7dc76f2016-03-10 11:14:40 -08001472#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473#pragma warning ( push )
1474#pragma warning ( disable : 4701 )
1475#endif
1476
herbf553e4e2015-11-09 08:51:56 -08001477////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478
herbd4c24f62015-12-07 12:12:29 -08001479class DrawOneGlyph {
1480public:
1481 DrawOneGlyph(const SkDraw& draw, const SkPaint& paint, SkGlyphCache* cache, SkBlitter* blitter)
1482 : fUseRegionToDraw(UsingRegionToDraw(draw.fRC))
1483 , fGlyphCache(cache)
1484 , fBlitter(blitter)
1485 , fClip(fUseRegionToDraw ? &draw.fRC->bwRgn() : nullptr)
1486 , fDraw(draw)
1487 , fPaint(paint)
1488 , fClipBounds(PickClipBounds(draw)) { }
herb11a7f7f2015-11-24 12:41:00 -08001489
herbd4c24f62015-12-07 12:12:29 -08001490 void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
1491 position += rounding;
herbd4c24f62015-12-07 12:12:29 -08001492 // Prevent glyphs from being drawn outside of or straddling the edge of device space.
mtklein875e13c2016-06-19 05:28:33 -07001493 // Comparisons written a little weirdly so that NaN coordinates are treated safely.
1494 auto gt = [](float a, int b) { return !(a <= (float)b); };
1495 auto lt = [](float a, int b) { return !(a >= (float)b); };
1496 if (gt(position.fX, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1497 lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
1498 gt(position.fY, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1499 lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))) {
herbd4c24f62015-12-07 12:12:29 -08001500 return;
1501 }
herb11a7f7f2015-11-24 12:41:00 -08001502
benjaminwagner77a9cc12016-03-25 11:24:30 -07001503 int left = SkScalarFloorToInt(position.fX);
1504 int top = SkScalarFloorToInt(position.fY);
herbd4c24f62015-12-07 12:12:29 -08001505 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1506
1507 left += glyph.fLeft;
1508 top += glyph.fTop;
1509
1510 int right = left + glyph.fWidth;
1511 int bottom = top + glyph.fHeight;
1512
1513 SkMask mask;
1514 mask.fBounds.set(left, top, right, bottom);
mtklein875e13c2016-06-19 05:28:33 -07001515 SkASSERT(!mask.fBounds.isEmpty());
herbd4c24f62015-12-07 12:12:29 -08001516
1517 if (fUseRegionToDraw) {
1518 SkRegion::Cliperator clipper(*fClip, mask.fBounds);
1519
1520 if (!clipper.done() && this->getImageData(glyph, &mask)) {
1521 const SkIRect& cr = clipper.rect();
1522 do {
1523 this->blitMask(mask, cr);
1524 clipper.next();
1525 } while (!clipper.done());
1526 }
1527 } else {
1528 SkIRect storage;
1529 SkIRect* bounds = &mask.fBounds;
1530
1531 // this extra test is worth it, assuming that most of the time it succeeds
1532 // since we can avoid writing to storage
1533 if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) {
1534 if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds))
1535 return;
1536 bounds = &storage;
1537 }
1538
1539 if (this->getImageData(glyph, &mask)) {
1540 this->blitMask(mask, *bounds);
1541 }
1542 }
1543 }
1544
1545private:
1546 static bool UsingRegionToDraw(const SkRasterClip* rClip) {
1547 return rClip->isBW() && !rClip->isRect();
1548 }
1549
1550 static SkIRect PickClipBounds(const SkDraw& draw) {
1551 const SkRasterClip& rasterClip = *draw.fRC;
1552
1553 if (rasterClip.isBW()) {
1554 return rasterClip.bwRgn().getBounds();
1555 } else {
1556 return rasterClip.aaRgn().getBounds();
1557 }
1558 }
1559
1560 bool getImageData(const SkGlyph& glyph, SkMask* mask) {
1561 uint8_t* bits = (uint8_t*)(fGlyphCache->findImage(glyph));
1562 if (nullptr == bits) {
1563 return false; // can't rasterize glyph
1564 }
1565 mask->fImage = bits;
1566 mask->fRowBytes = glyph.rowBytes();
1567 mask->fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1568 return true;
1569 }
1570
herb11a7f7f2015-11-24 12:41:00 -08001571 void blitMask(const SkMask& mask, const SkIRect& clip) const {
1572 if (SkMask::kARGB32_Format == mask.fFormat) {
herbd4c24f62015-12-07 12:12:29 -08001573 SkBitmap bm;
1574 bm.installPixels(
1575 SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()),
1576 (SkPMColor*)mask.fImage, mask.fRowBytes);
1577
1578 fDraw.drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), fPaint);
herb11a7f7f2015-11-24 12:41:00 -08001579 } else {
1580 fBlitter->blitMask(mask, clip);
1581 }
1582 }
1583
herbd4c24f62015-12-07 12:12:29 -08001584 const bool fUseRegionToDraw;
1585 SkGlyphCache * const fGlyphCache;
1586 SkBlitter * const fBlitter;
1587 const SkRegion* const fClip;
1588 const SkDraw& fDraw;
1589 const SkPaint& fPaint;
1590 const SkIRect fClipBounds;
herb11a7f7f2015-11-24 12:41:00 -08001591};
1592
herbd4c24f62015-12-07 12:12:29 -08001593////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594
brianosmana1e8f8d2016-04-08 06:47:54 -07001595uint32_t SkDraw::scalerContextFlags() const {
1596 uint32_t flags = SkPaint::kBoostContrast_ScalerContextFlag;
Brian Osman7039f742016-11-01 15:56:16 -04001597 if (!fDevice->imageInfo().colorSpace()) {
brianosmana1e8f8d2016-04-08 06:47:54 -07001598 flags |= SkPaint::kFakeGamma_ScalerContextFlag;
1599 }
1600 return flags;
bungemanf6d1e602016-02-22 13:20:28 -08001601}
1602
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603void SkDraw::drawText(const char text[], size_t byteLength,
1604 SkScalar x, SkScalar y, const SkPaint& paint) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001605 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606
reed@android.comf2b98d62010-12-20 18:26:13 +00001607 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608
1609 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001610 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 return;
1612 }
1613
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001614 // SkScalarRec doesn't currently have a way of representing hairline stroke and
1615 // will fill if its frame-width is 0.
reed@google.comed43dff2013-06-04 16:56:27 +00001616 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 this->drawText_asPaths(text, byteLength, x, y, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618 return;
1619 }
1620
brianosmana1e8f8d2016-04-08 06:47:54 -07001621 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), fMatrix);
herb11a7f7f2015-11-24 12:41:00 -08001622
1623 // The Blitter Choose needs to be live while using the blitter below.
1624 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1625 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001626 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627
herbe59124e2015-11-18 10:54:39 -08001628 SkFindAndPlaceGlyph::ProcessText(
herb4c11b3f2015-11-20 13:53:12 -08001629 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001630 {x, y}, *fMatrix, paint.getTextAlign(), cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631}
1632
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633//////////////////////////////////////////////////////////////////////////////
1634
reed@google.comed43dff2013-06-04 16:56:27 +00001635void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -07001636 const SkScalar pos[], int scalarsPerPosition,
1637 const SkPoint& offset, const SkPaint& origPaint) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001638 // setup our std paint, in hopes of getting hits in the cache
1639 SkPaint paint(origPaint);
1640 SkScalar matrixScale = paint.setupForAsPaths();
1641
reed@google.com5a649022013-06-05 18:00:30 +00001642 SkMatrix matrix;
1643 matrix.setScale(matrixScale, matrixScale);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001644
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001645 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
1646 paint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07001647 paint.setPathEffect(nullptr);
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001648
robertphillipse34f17d2016-07-19 07:59:22 -07001649 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
1650 paint.isDevKernText(),
1651 true);
brianosmana1e8f8d2016-04-08 06:47:54 -07001652 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), nullptr);
reed@google.comed43dff2013-06-04 16:56:27 +00001653
1654 const char* stop = text + byteLength;
bungeman79738cc2015-03-11 14:05:29 -07001655 SkTextAlignProc alignProc(paint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -07001656 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001657
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001658 // Now restore the original settings, so we "draw" with whatever style/stroking.
1659 paint.setStyle(origPaint.getStyle());
Mike Reed693fdbd2017-01-12 10:13:40 -05001660 paint.setPathEffect(origPaint.refPathEffect());
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001661
reed@google.comed43dff2013-06-04 16:56:27 +00001662 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -08001663 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
reed@google.comed43dff2013-06-04 16:56:27 +00001664 if (glyph.fWidth) {
1665 const SkPath* path = cache->findPath(glyph);
1666 if (path) {
kkinnunencb9a2c82014-06-12 23:06:28 -07001667 SkPoint tmsLoc;
1668 tmsProc(pos, &tmsLoc);
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001669 SkPoint loc;
kkinnunencb9a2c82014-06-12 23:06:28 -07001670 alignProc(tmsLoc, glyph, &loc);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001671
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001672 matrix[SkMatrix::kMTransX] = loc.fX;
1673 matrix[SkMatrix::kMTransY] = loc.fY;
reed@google.com5a649022013-06-05 18:00:30 +00001674 if (fDevice) {
1675 fDevice->drawPath(*this, *path, paint, &matrix, false);
1676 } else {
1677 this->drawPath(*path, paint, &matrix, false);
1678 }
reed@google.comed43dff2013-06-04 16:56:27 +00001679 }
1680 }
1681 pos += scalarsPerPosition;
1682 }
1683}
1684
reed@android.com8a1c16f2008-12-17 15:59:43 +00001685void SkDraw::drawPosText(const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -07001686 const SkScalar pos[], int scalarsPerPosition,
1687 const SkPoint& offset, const SkPaint& paint) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001688 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1690
reed@android.comf2b98d62010-12-20 18:26:13 +00001691 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692
1693 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001694 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 return;
1696 }
1697
reed@google.comed43dff2013-06-04 16:56:27 +00001698 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
fmalita05c4a432014-09-29 06:29:53 -07001699 this->drawPosText_asPaths(text, byteLength, pos, scalarsPerPosition, offset, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001700 return;
1701 }
1702
brianosmana1e8f8d2016-04-08 06:47:54 -07001703 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), fMatrix);
herbd4c24f62015-12-07 12:12:29 -08001704
herb9be5ff62015-11-11 11:30:11 -08001705 // The Blitter Choose needs to be live while using the blitter below.
herb11a7f7f2015-11-24 12:41:00 -08001706 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1707 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001708 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
herbd4c24f62015-12-07 12:12:29 -08001709 SkPaint::Align textAlignment = paint.getTextAlign();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710
herb9be5ff62015-11-11 11:30:11 -08001711 SkFindAndPlaceGlyph::ProcessPosText(
herb4c11b3f2015-11-20 13:53:12 -08001712 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001713 offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714}
1715
bungemand7dc76f2016-03-10 11:14:40 -08001716#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717#pragma warning ( pop )
1718#endif
1719
1720///////////////////////////////////////////////////////////////////////////////
1721
reed5dc6b7d2015-04-14 10:40:44 -07001722static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
1724}
1725
1726static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
1727 const SkPoint texs[], SkMatrix* matrix) {
1728 SkPoint src[3], dst[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00001729
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 src[0] = texs[state.f0];
1731 src[1] = texs[state.f1];
1732 src[2] = texs[state.f2];
1733 dst[0] = verts[state.f0];
1734 dst[1] = verts[state.f1];
1735 dst[2] = verts[state.f2];
1736 return matrix->setPolyToPoly(src, dst, 3);
1737}
1738
1739class SkTriColorShader : public SkShader {
1740public:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001741 SkTriColorShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001743 class TriColorShaderContext : public SkShader::Context {
1744 public:
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001745 TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001746 virtual ~TriColorShaderContext();
mtklein36352bf2015-03-25 18:17:31 -07001747 void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001748
1749 private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001750 bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
1751
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001752 SkMatrix fDstToUnit;
1753 SkPMColor fColors[3];
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001754 bool fSetup;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001755
1756 typedef SkShader::Context INHERITED;
1757 };
reed@google.coma76de3d2011-01-13 18:30:42 +00001758
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001759 struct TriColorShaderData {
1760 const SkPoint* pts;
1761 const SkColor* colors;
1762 const VertState *state;
1763 };
1764
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001765 SK_TO_STRING_OVERRIDE()
mtklein7e44bb12015-01-07 09:06:08 -08001766
1767 // For serialization. This will never be called.
halcanary96fcdcc2015-08-27 07:41:13 -07001768 Factory getFactory() const override { sk_throw(); return nullptr; }
djsollen@google.comba28d032012-03-26 17:57:35 +00001769
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001770 // Supply setup data to context from drawing setup
1771 void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; }
1772
1773 // Take the setup data from context when needed.
1774 TriColorShaderData* takeSetupData() {
1775 TriColorShaderData *data = fSetupData;
1776 fSetupData = NULL;
1777 return data;
1778 }
1779
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780protected:
Herb Derby83e939b2017-02-07 14:25:11 -05001781 Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
1782 return alloc->make<TriColorShaderContext>(*this, rec);
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +00001783 }
1784
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001786 TriColorShaderData *fSetupData;
1787
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788 typedef SkShader INHERITED;
1789};
1790
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001791bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[],
1792 int index0, int index1, int index2) {
reed@google.coma76de3d2011-01-13 18:30:42 +00001793
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794 fColors[0] = SkPreMultiplyColor(colors[index0]);
1795 fColors[1] = SkPreMultiplyColor(colors[index1]);
1796 fColors[2] = SkPreMultiplyColor(colors[index2]);
reed@google.coma76de3d2011-01-13 18:30:42 +00001797
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 SkMatrix m, im;
1799 m.reset();
1800 m.set(0, pts[index1].fX - pts[index0].fX);
1801 m.set(1, pts[index2].fX - pts[index0].fX);
1802 m.set(2, pts[index0].fX);
1803 m.set(3, pts[index1].fY - pts[index0].fY);
1804 m.set(4, pts[index2].fY - pts[index0].fY);
1805 m.set(5, pts[index0].fY);
1806 if (!m.invert(&im)) {
1807 return false;
1808 }
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001809 // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix
1810 // as our interators are intrinsically tied to the vertices, and nothing else.
1811 SkMatrix ctmInv;
1812 if (!this->getCTM().invert(&ctmInv)) {
1813 return false;
1814 }
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001815 // TODO replace INV(m) * INV(ctm) with INV(ctm * m)
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001816 fDstToUnit.setConcat(im, ctmInv);
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001817 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818}
1819
1820#include "SkColorPriv.h"
reed@android.com689411a2008-12-18 02:52:32 +00001821#include "SkComposeShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822
1823static int ScalarTo256(SkScalar v) {
benjaminwagner43437c22016-02-24 10:57:47 -08001824 return static_cast<int>(SkScalarPin(v, 0, 1) * 256 + 0.5);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825}
1826
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001827SkTriColorShader::SkTriColorShader()
1828 : INHERITED(NULL)
1829 , fSetupData(NULL) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001830
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001831SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader,
1832 const ContextRec& rec)
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001833 : INHERITED(shader, rec)
1834 , fSetup(false) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001835
1836SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {}
1837
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001838void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001839 SkTriColorShader* parent = static_cast<SkTriColorShader*>(const_cast<SkShader*>(&fShader));
1840 TriColorShaderData* set = parent->takeSetupData();
1841 if (set) {
1842 fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2);
1843 }
1844
1845 if (!fSetup) {
1846 // Invalid matrices. Not checked before so no need to assert.
1847 return;
1848 }
1849
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001850 const int alphaScale = Sk255To256(this->getPaintAlpha());
1851
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852 SkPoint src;
reed@google.coma76de3d2011-01-13 18:30:42 +00001853
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854 for (int i = 0; i < count; i++) {
1855 fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
1856 x += 1;
reed@google.coma76de3d2011-01-13 18:30:42 +00001857
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858 int scale1 = ScalarTo256(src.fX);
1859 int scale2 = ScalarTo256(src.fY);
1860 int scale0 = 256 - scale1 - scale2;
1861 if (scale0 < 0) {
1862 if (scale1 > scale2) {
1863 scale2 = 256 - scale1;
1864 } else {
1865 scale1 = 256 - scale2;
1866 }
1867 scale0 = 0;
1868 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001869
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001870 if (256 != alphaScale) {
1871 scale0 = SkAlphaMul(scale0, alphaScale);
1872 scale1 = SkAlphaMul(scale1, alphaScale);
1873 scale2 = SkAlphaMul(scale2, alphaScale);
1874 }
1875
reed@android.com8a1c16f2008-12-17 15:59:43 +00001876 dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001877 SkAlphaMulQ(fColors[1], scale1) +
1878 SkAlphaMulQ(fColors[2], scale2);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001879 }
1880}
1881
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001882#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001883void SkTriColorShader::toString(SkString* str) const {
1884 str->append("SkTriColorShader: (");
1885
1886 this->INHERITED::toString(str);
1887
1888 str->append(")");
1889}
1890#endif
1891
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
1893 const SkPoint vertices[], const SkPoint textures[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001894 const SkColor colors[], SkBlendMode bmode,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 const uint16_t indices[], int indexCount,
1896 const SkPaint& paint) const {
bsalomon49f085d2014-09-05 13:34:00 -07001897 SkASSERT(0 == count || vertices);
reed@google.coma76de3d2011-01-13 18:30:42 +00001898
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899 // abort early if there is nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001900 if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901 return;
1902 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001903
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904 // transform out vertices into device coordinates
1905 SkAutoSTMalloc<16, SkPoint> storage(count);
1906 SkPoint* devVerts = storage.get();
1907 fMatrix->mapPoints(devVerts, vertices, count);
reed@google.coma76de3d2011-01-13 18:30:42 +00001908
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 /*
1910 We can draw the vertices in 1 of 4 ways:
1911
1912 - solid color (no shader/texture[], no colors[])
1913 - just colors (no shader/texture[], has colors[])
1914 - just texture (has shader/texture[], no colors[])
1915 - colors * texture (has shader/texture[], has colors[])
reed@google.coma76de3d2011-01-13 18:30:42 +00001916
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917 Thus for texture drawing, we need both texture[] and a shader.
1918 */
1919
reed8a21c9f2016-03-08 18:50:00 -08001920 auto triShader = sk_make_sp<SkTriColorShader>();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921 SkPaint p(paint);
1922
1923 SkShader* shader = p.getShader();
halcanary96fcdcc2015-08-27 07:41:13 -07001924 if (nullptr == shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 // if we have no shader, we ignore the texture coordinates
halcanary96fcdcc2015-08-27 07:41:13 -07001926 textures = nullptr;
1927 } else if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 // if we don't have texture coordinates, ignore the shader
halcanary96fcdcc2015-08-27 07:41:13 -07001929 p.setShader(nullptr);
1930 shader = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001931 }
1932
1933 // setup the custom shader (if needed)
bsalomon49f085d2014-09-05 13:34:00 -07001934 if (colors) {
halcanary96fcdcc2015-08-27 07:41:13 -07001935 if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936 // just colors (no texture)
reed8a21c9f2016-03-08 18:50:00 -08001937 p.setShader(triShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 } else {
1939 // colors * texture
1940 SkASSERT(shader);
Mike Reed7d954ad2016-10-28 15:42:34 -04001941 p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), bmode));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001942 }
1943 }
1944
reed41e010c2015-06-09 12:16:53 -07001945 SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001946 // Abort early if we failed to create a shader context.
reed@google.comea033602012-12-14 13:13:55 +00001947 if (blitter->isNullBlitter()) {
1948 return;
1949 }
1950
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 // setup our state and function pointer for iterating triangles
1952 VertState state(count, indices, indexCount);
1953 VertState::Proc vertProc = state.chooseProc(vmode);
reed@google.coma76de3d2011-01-13 18:30:42 +00001954
bsalomon49f085d2014-09-05 13:34:00 -07001955 if (textures || colors) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001956 SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state };
1957
reed@android.com8a1c16f2008-12-17 15:59:43 +00001958 while (vertProc(&state)) {
Florin Malita00dca8c2017-01-27 12:05:27 -05001959 auto* blitterPtr = blitter.get();
1960
1961 SkTLazy<SkLocalMatrixShader> localShader;
1962 SkTLazy<SkAutoBlitterChoose> localBlitter;
1963
bsalomon49f085d2014-09-05 13:34:00 -07001964 if (textures) {
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001965 SkMatrix tempM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 if (texture_to_matrix(state, vertices, textures, &tempM)) {
Florin Malita00dca8c2017-01-27 12:05:27 -05001967 localShader.init(p.refShader(), tempM);
1968
1969 SkPaint localPaint(p);
1970 localPaint.setShader(sk_ref_sp(localShader.get()));
1971
1972 blitterPtr = localBlitter.init(fDst, *fMatrix, localPaint)->get();
1973 if (blitterPtr->isNullBlitter()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974 continue;
1975 }
1976 }
1977 }
bsalomon49f085d2014-09-05 13:34:00 -07001978 if (colors) {
reed8a21c9f2016-03-08 18:50:00 -08001979 triShader->bindSetupData(&verticesSetup);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980 }
reed@google.com045e62d2011-10-24 12:19:46 +00001981
1982 SkPoint tmp[] = {
1983 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
1984 };
Florin Malita00dca8c2017-01-27 12:05:27 -05001985 SkScan::FillTriangle(tmp, *fRC, blitterPtr);
1986 triShader->bindSetupData(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001988 } else {
commit-bot@chromium.org559a8832014-05-30 10:08:22 +00001989 // no colors[] and no texture, stroke hairlines with paint's color.
reed5dc6b7d2015-04-14 10:40:44 -07001990 SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
reed@google.com045e62d2011-10-24 12:19:46 +00001991 const SkRasterClip& clip = *fRC;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992 while (vertProc(&state)) {
reed5dc6b7d2015-04-14 10:40:44 -07001993 SkPoint array[] = {
1994 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
1995 };
1996 hairProc(array, 4, clip, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997 }
1998 }
1999}
2000
reed@google.com0a0a2362011-03-23 13:51:55 +00002001///////////////////////////////////////////////////////////////////////////////
2002///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003
2004#ifdef SK_DEBUG
2005
reed@android.comf2b98d62010-12-20 18:26:13 +00002006void SkDraw::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -07002007 SkASSERT(fMatrix != nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -07002008 SkASSERT(fRC != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009
reed@google.com045e62d2011-10-24 12:19:46 +00002010 const SkIRect& cr = fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011 SkIRect br;
2012
reed41e010c2015-06-09 12:16:53 -07002013 br.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014 SkASSERT(cr.isEmpty() || br.contains(cr));
2015}
2016
2017#endif
2018
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019////////////////////////////////////////////////////////////////////////////////////////////////
2020
2021#include "SkPath.h"
2022#include "SkDraw.h"
2023#include "SkRegion.h"
2024#include "SkBlitter.h"
2025
2026static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
reedb07a94f2014-11-19 05:03:18 -08002027 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 SkIRect* bounds) {
2029 if (devPath.isEmpty()) {
2030 return false;
2031 }
2032
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 // init our bounds from the path
reed11fa2242015-03-13 06:08:28 -07002034 *bounds = devPath.getBounds().makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
reed@google.coma76de3d2011-01-13 18:30:42 +00002035
tomhudson@google.com6db75fc2012-03-23 14:46:48 +00002036 SkIPoint margin = SkIPoint::Make(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037 if (filter) {
2038 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00002039
bungeman@google.com5af16f82011-09-02 15:06:44 +00002040 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00002041
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042 srcM.fBounds = *bounds;
2043 srcM.fFormat = SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
2045 return false;
2046 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047 }
2048
bungeman@google.com5af16f82011-09-02 15:06:44 +00002049 // (possibly) trim the bounds to reflect the clip
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050 // (plus whatever slop the filter needs)
bungeman@google.com5af16f82011-09-02 15:06:44 +00002051 if (clipBounds) {
reed@android.com35555912009-03-16 18:46:55 +00002052 // Ugh. Guard against gigantic margins from wacky filters. Without this
2053 // check we can request arbitrary amounts of slop beyond our visible
2054 // clip, and bring down the renderer (at least on finite RAM machines
2055 // like handsets, etc.). Need to balance this invented value between
2056 // quality of large filters like blurs, and the corresponding memory
2057 // requests.
2058 static const int MAX_MARGIN = 128;
reed11fa2242015-03-13 06:08:28 -07002059 if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
2060 SkMin32(margin.fY, MAX_MARGIN)))) {
bungeman@google.com5af16f82011-09-02 15:06:44 +00002061 return false;
2062 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063 }
2064
2065 return true;
2066}
2067
bsalomon055e1922016-05-06 07:22:58 -07002068static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
2069 SkStrokeRec::InitStyle style) {
reed41e010c2015-06-09 12:16:53 -07002070 SkDraw draw;
2071 if (!draw.fDst.reset(mask)) {
2072 return;
2073 }
2074
reed@google.com045e62d2011-10-24 12:19:46 +00002075 SkRasterClip clip;
2076 SkMatrix matrix;
2077 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078
reed@google.com045e62d2011-10-24 12:19:46 +00002079 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
2081 -SkIntToScalar(mask.fBounds.fTop));
2082
reed@google.com045e62d2011-10-24 12:19:46 +00002083 draw.fRC = &clip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084 draw.fMatrix = &matrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 paint.setAntiAlias(true);
bsalomon055e1922016-05-06 07:22:58 -07002086 switch (style) {
2087 case SkStrokeRec::kHairline_InitStyle:
2088 SkASSERT(!paint.getStrokeWidth());
2089 paint.setStyle(SkPaint::kStroke_Style);
2090 break;
2091 case SkStrokeRec::kFill_InitStyle:
2092 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
2093 break;
2094
2095 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096 draw.drawPath(devPath, paint);
2097}
2098
2099bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +00002100 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002101 SkMask* mask, SkMask::CreateMode mode,
bsalomon055e1922016-05-06 07:22:58 -07002102 SkStrokeRec::InitStyle style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 if (SkMask::kJustRenderImage_CreateMode != mode) {
2104 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
2105 return false;
2106 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002107
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
2109 mask->fFormat = SkMask::kA8_Format;
2110 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00002111 size_t size = mask->computeImageSize();
2112 if (0 == size) {
2113 // we're too big to allocate the mask, abort
2114 return false;
2115 }
2116 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 memset(mask->fImage, 0, mask->computeImageSize());
2118 }
2119
2120 if (SkMask::kJustComputeBounds_CreateMode != mode) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002121 draw_into_mask(*mask, devPath, style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002123
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 return true;
2125}