blob: d2c6bb131e382ede6b10841023f645d3492dc288 [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"
10#include "SkBlitter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkCanvas.h"
12#include "SkColorPriv.h"
13#include "SkDevice.h"
reed@google.com1c028bd2013-08-28 15:23:19 +000014#include "SkDeviceLooper.h"
herbe5911c92015-11-09 13:15:21 -080015#include "SkFindAndPlaceGlyph.h"
bungeman@google.com2211b622012-01-13 15:02:58 +000016#include "SkFixed.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkMaskFilter.h"
herbf553e4e2015-11-09 08:51:56 -080018#include "SkMatrix.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPaint.h"
20#include "SkPathEffect.h"
reed@google.com045e62d2011-10-24 12:19:46 +000021#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkRasterizer.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000023#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkScan.h"
25#include "SkShader.h"
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000026#include "SkSmallAllocator.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +000027#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkStroke.h"
halcanary435657f2015-09-15 12:53:07 -070029#include "SkStrokeRec.h"
herbf553e4e2015-11-09 08:51:56 -080030#include "SkTemplates.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070031#include "SkTextMapStateProc.h"
reed@google.com32e5d972011-07-27 18:25:57 +000032#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000033#include "SkUtils.h"
commit-bot@chromium.org559a8832014-05-30 10:08:22 +000034#include "SkVertState.h"
reedcfb6bdf2016-03-29 11:32:50 -070035#include "SkXfermode.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
reed@android.com8a1c16f2008-12-17 15:59:43 +000037#include "SkBitmapProcShader.h"
38#include "SkDrawProcs.h"
reed@google.comae573582013-01-03 15:22:40 +000039#include "SkMatrixUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
41//#define TRACE_BITMAP_DRAWS
42
herbd4c24f62015-12-07 12:12:29 -080043// Helper function to fix code gen bug on ARM64.
44// See SkFindAndPlaceGlyph.h for more details.
45void FixGCC49Arm64Bug(int v) { }
reed@android.com8a1c16f2008-12-17 15:59:43 +000046
reed@google.comfd4236e2011-07-25 21:16:22 +000047/** Helper for allocating small blitters on the stack.
48 */
reed@google.com40c2ba22011-07-25 19:41:22 +000049class SkAutoBlitterChoose : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000050public:
reed@google.com1d6ee0b2011-07-05 17:44:56 +000051 SkAutoBlitterChoose() {
halcanary96fcdcc2015-08-27 07:41:13 -070052 fBlitter = nullptr;
reed@google.com1d6ee0b2011-07-05 17:44:56 +000053 }
reed41e010c2015-06-09 12:16:53 -070054 SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix,
reed@google.com126f7f52013-11-07 16:06:53 +000055 const SkPaint& paint, bool drawCoverage = false) {
reed41e010c2015-06-09 12:16:53 -070056 fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAllocator, drawCoverage);
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000057 }
herbe59124e2015-11-18 10:54:39 -080058
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 SkBlitter* operator->() { return fBlitter; }
60 SkBlitter* get() const { return fBlitter; }
61
reed41e010c2015-06-09 12:16:53 -070062 void choose(const SkPixmap& dst, const SkMatrix& matrix,
krajcevski53f09592014-08-06 11:12:14 -070063 const SkPaint& paint, bool drawCoverage = false) {
reed@google.com1d6ee0b2011-07-05 17:44:56 +000064 SkASSERT(!fBlitter);
reed41e010c2015-06-09 12:16:53 -070065 fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAllocator, drawCoverage);
reed@google.com1d6ee0b2011-07-05 17:44:56 +000066 }
67
reed@android.com8a1c16f2008-12-17 15:59:43 +000068private:
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000069 // Owned by fAllocator, which will handle the delete.
70 SkBlitter* fBlitter;
71 SkTBlitterAllocator fAllocator;
reed@android.com8a1c16f2008-12-17 15:59:43 +000072};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +000073#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose)
reed@android.com8a1c16f2008-12-17 15:59:43 +000074
reed@google.com40c2ba22011-07-25 19:41:22 +000075/**
76 * Since we are providing the storage for the shader (to avoid the perf cost
77 * of calling new) we insist that in our destructor we can account for all
78 * owners of the shader.
79 */
80class SkAutoBitmapShaderInstall : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081public:
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +000082 SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint,
halcanary96fcdcc2015-08-27 07:41:13 -070083 const SkMatrix* localMatrix = nullptr)
reed@google.com40c2ba22011-07-25 19:41:22 +000084 : fPaint(paint) /* makes a copy of the paint */ {
reed8a21c9f2016-03-08 18:50:00 -080085 fPaint.setShader(SkMakeBitmapShader(src, SkShader::kClamp_TileMode,
86 SkShader::kClamp_TileMode, localMatrix, &fAllocator));
reed@google.com40c2ba22011-07-25 19:41:22 +000087 // we deliberately left the shader with an owner-count of 2
reed8a21c9f2016-03-08 18:50:00 -080088 fPaint.getShader()->ref();
reed@google.com40c2ba22011-07-25 19:41:22 +000089 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 }
reed@google.com82065d62011-02-07 15:30:46 +000091
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 ~SkAutoBitmapShaderInstall() {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000093 // since fAllocator will destroy shader, we insist that owners == 2
94 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000095
halcanary96fcdcc2015-08-27 07:41:13 -070096 fPaint.setShader(nullptr); // unref the shader by 1
reed@android.com8a1c16f2008-12-17 15:59:43 +000097
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 }
reed@google.com82065d62011-02-07 15:30:46 +000099
reed@google.com40c2ba22011-07-25 19:41:22 +0000100 // return the new paint that has the shader applied
101 const SkPaint& paintWithShader() const { return fPaint; }
102
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103private:
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000104 // copy of caller's paint (which we then modify)
105 SkPaint fPaint;
106 // Stores the shader.
107 SkTBlitterAllocator fAllocator;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000109#define SkAutoBitmapShaderInstall(...) SK_REQUIRE_LOCAL_VAR(SkAutoBitmapShaderInstall)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111///////////////////////////////////////////////////////////////////////////////
112
reed@android.comf2b98d62010-12-20 18:26:13 +0000113SkDraw::SkDraw() {
114 sk_bzero(this, sizeof(*this));
115}
116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117SkDraw::SkDraw(const SkDraw& src) {
118 memcpy(this, &src, sizeof(*this));
119}
120
reed@google.com4bbdeac2013-01-24 21:03:11 +0000121bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
122 if (fRC->isEmpty()) {
123 return false;
124 }
125
126 SkMatrix inverse;
127 if (!fMatrix->invert(&inverse)) {
128 return false;
129 }
130
131 SkIRect devBounds = fRC->getBounds();
132 // outset to have slop for antialasing and hairlines
133 devBounds.outset(1, 1);
134 inverse.mapRect(localBounds, SkRect::Make(devBounds));
135 return true;
136}
137
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138///////////////////////////////////////////////////////////////////////////////
139
140typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
141
142static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +0000143 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144}
145
146static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
147
148static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000149 sk_memset32((uint32_t*)pixels, data, SkToInt(bytes >> 2));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150}
151
152static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000153 sk_memset16((uint16_t*)pixels, data, SkToInt(bytes >> 1));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154}
155
156static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
157 memset(pixels, data, bytes);
158}
159
reed41e010c2015-06-09 12:16:53 -0700160static BitmapXferProc ChooseBitmapXferProc(const SkPixmap& dst, const SkPaint& paint,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 uint32_t* data) {
162 // todo: we can apply colorfilter up front if no shader, so we wouldn't
163 // need to abort this fastpath
164 if (paint.getShader() || paint.getColorFilter()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700165 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 }
167
reed@android.com845fdac2009-06-23 03:01:32 +0000168 SkXfermode::Mode mode;
mike@reedtribe.orgbe2aa2a2011-11-17 02:32:04 +0000169 if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700170 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 // collaps modes based on color...
reed@android.com845fdac2009-06-23 03:01:32 +0000176 if (SkXfermode::kSrcOver_Mode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 unsigned alpha = SkColorGetA(color);
178 if (0 == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000179 mode = SkXfermode::kDst_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 } else if (0xFF == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000181 mode = SkXfermode::kSrc_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 }
183 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 switch (mode) {
reed@android.com845fdac2009-06-23 03:01:32 +0000186 case SkXfermode::kClear_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187// SkDebugf("--- D_Clear_BitmapXferProc\n");
188 return D_Clear_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000189 case SkXfermode::kDst_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190// SkDebugf("--- D_Dst_BitmapXferProc\n");
191 return D_Dst_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000192 case SkXfermode::kSrc_Mode: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000194 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 */
196 SkPMColor pmc = SkPreMultiplyColor(color);
reed41e010c2015-06-09 12:16:53 -0700197 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000198 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 if (data) {
200 *data = pmc;
201 }
202// SkDebugf("--- D32_Src_BitmapXferProc\n");
203 return D32_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000204 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 if (data) {
206 *data = SkPixel32ToPixel16(pmc);
207 }
208// SkDebugf("--- D16_Src_BitmapXferProc\n");
209 return D16_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000210 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 if (data) {
212 *data = SkGetPackedA32(pmc);
213 }
214// SkDebugf("--- DA8_Src_BitmapXferProc\n");
215 return DA8_Src_BitmapXferProc;
216 default:
217 break;
218 }
219 break;
220 }
221 default:
222 break;
223 }
halcanary96fcdcc2015-08-27 07:41:13 -0700224 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225}
226
reed41e010c2015-06-09 12:16:53 -0700227static void CallBitmapXferProc(const SkPixmap& dst, const SkIRect& rect, BitmapXferProc proc,
228 uint32_t procData) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 int shiftPerPixel;
reed41e010c2015-06-09 12:16:53 -0700230 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000231 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 shiftPerPixel = 2;
233 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000234 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 shiftPerPixel = 1;
236 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000237 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 shiftPerPixel = 0;
239 break;
240 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000241 SkDEBUGFAIL("Can't use xferproc on this config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 return;
243 }
244
reed41e010c2015-06-09 12:16:53 -0700245 uint8_t* pixels = (uint8_t*)dst.writable_addr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 SkASSERT(pixels);
reed41e010c2015-06-09 12:16:53 -0700247 const size_t rowBytes = dst.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 const int widthBytes = rect.width() << shiftPerPixel;
249
250 // skip down to the first scanline and X position
251 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
252 for (int scans = rect.height() - 1; scans >= 0; --scans) {
253 proc(pixels, widthBytes, procData);
254 pixels += rowBytes;
255 }
256}
257
258void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000259 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260
reed@google.com045e62d2011-10-24 12:19:46 +0000261 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 return;
263 }
264
265 SkIRect devRect;
reed41e010c2015-06-09 12:16:53 -0700266 devRect.set(0, 0, fDst.width(), fDst.height());
reed@google.coma76de3d2011-01-13 18:30:42 +0000267
reed@google.com045e62d2011-10-24 12:19:46 +0000268 if (fRC->isBW()) {
269 /* If we don't have a shader (i.e. we're just a solid color) we may
270 be faster to operate directly on the device bitmap, rather than invoking
271 a blitter. Esp. true for xfermodes, which require a colorshader to be
272 present, which is just redundant work. Since we're drawing everywhere
273 in the clip, we don't have to worry about antialiasing.
274 */
275 uint32_t procData = 0; // to avoid the warning
reed41e010c2015-06-09 12:16:53 -0700276 BitmapXferProc proc = ChooseBitmapXferProc(fDst, paint, &procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000277 if (proc) {
278 if (D_Dst_BitmapXferProc == proc) { // nothing to do
279 return;
280 }
281
282 SkRegion::Iterator iter(fRC->bwRgn());
283 while (!iter.done()) {
reed41e010c2015-06-09 12:16:53 -0700284 CallBitmapXferProc(fDst, iter.rect(), proc, procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000285 iter.next();
286 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 return;
288 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 }
reed@google.com045e62d2011-10-24 12:19:46 +0000290
291 // normal case: use a blitter
reed41e010c2015-06-09 12:16:53 -0700292 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000293 SkScan::FillIRect(devRect, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294}
295
296///////////////////////////////////////////////////////////////////////////////
297
298struct PtProcRec {
299 SkCanvas::PointMode fMode;
300 const SkPaint* fPaint;
301 const SkRegion* fClip;
reed@google.com045e62d2011-10-24 12:19:46 +0000302 const SkRasterClip* fRC;
reed@google.coma76de3d2011-01-13 18:30:42 +0000303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 // computed values
305 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000306
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
308 SkBlitter*);
309
310 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000311 const SkRasterClip*);
312 Proc chooseProc(SkBlitter** blitter);
313
314private:
315 SkAAClipBlitterWrapper fWrapper;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316};
317
318static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
319 int count, SkBlitter* blitter) {
320 SkASSERT(rec.fClip->isRect());
321 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000322
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000324 int x = SkScalarFloorToInt(devPts[i].fX);
325 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 if (r.contains(x, y)) {
327 blitter->blitH(x, y, 1);
328 }
329 }
330}
331
332static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
333 const SkPoint devPts[], int count,
334 SkBlitter* blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +0000335 SkASSERT(rec.fRC->isRect());
336 const SkIRect& r = rec.fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700338 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
339 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000340
reed41e010c2015-06-09 12:16:53 -0700341 uint16_t* addr = dst->writable_addr16(0, 0);
342 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000343
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000345 int x = SkScalarFloorToInt(devPts[i].fX);
346 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 if (r.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
349 }
350 }
351}
352
reed@google.com2d47a212012-11-29 21:01:00 +0000353static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
354 const SkPoint devPts[], int count,
355 SkBlitter* blitter) {
356 SkASSERT(rec.fRC->isRect());
357 const SkIRect& r = rec.fRC->getBounds();
358 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700359 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
360 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000361
reed41e010c2015-06-09 12:16:53 -0700362 SkPMColor* addr = dst->writable_addr32(0, 0);
363 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000364
reed@google.com2d47a212012-11-29 21:01:00 +0000365 for (int i = 0; i < count; i++) {
366 int x = SkScalarFloorToInt(devPts[i].fX);
367 int y = SkScalarFloorToInt(devPts[i].fY);
368 if (r.contains(x, y)) {
369 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
370 }
371 }
372}
373
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
375 int count, SkBlitter* blitter) {
376 for (int i = 0; i < count; i++) {
reed@google.come1ca7052013-12-17 19:22:07 +0000377 int x = SkScalarFloorToInt(devPts[i].fX);
378 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 if (rec.fClip->contains(x, y)) {
380 blitter->blitH(x, y, 1);
381 }
382 }
383}
384
385static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
386 int count, SkBlitter* blitter) {
387 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700388 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 }
390}
391
392static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
393 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700394 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395}
396
397// aa versions
398
399static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
400 int count, SkBlitter* blitter) {
401 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700402 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 }
404}
405
406static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
407 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700408 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409}
410
411// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
412
413static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
414 int count, SkBlitter* blitter) {
415 const SkFixed radius = rec.fRadius;
416 for (int i = 0; i < count; i++) {
417 SkFixed x = SkScalarToFixed(devPts[i].fX);
418 SkFixed y = SkScalarToFixed(devPts[i].fY);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 SkXRect r;
421 r.fLeft = x - radius;
422 r.fTop = y - radius;
423 r.fRight = x + radius;
424 r.fBottom = y + radius;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000425
reed@google.com045e62d2011-10-24 12:19:46 +0000426 SkScan::FillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 }
428}
429
430static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
431 int count, SkBlitter* blitter) {
432 const SkFixed radius = rec.fRadius;
433 for (int i = 0; i < count; i++) {
434 SkFixed x = SkScalarToFixed(devPts[i].fX);
435 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 SkXRect r;
438 r.fLeft = x - radius;
439 r.fTop = y - radius;
440 r.fRight = x + radius;
441 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000442
reed@google.com045e62d2011-10-24 12:19:46 +0000443 SkScan::AntiFillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 }
445}
446
reed@android.comb4f404a2009-07-10 17:02:17 +0000447// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
reed@google.com045e62d2011-10-24 12:19:46 +0000449 const SkMatrix* matrix, const SkRasterClip* rc) {
ochang3ece53e2015-05-21 15:44:53 -0700450 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
451 return false;
452 }
453
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 if (paint.getPathEffect()) {
455 return false;
456 }
457 SkScalar width = paint.getStrokeWidth();
458 if (0 == width) {
459 fMode = mode;
460 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700461 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000462 fRC = rc;
reed@google.com2d47a212012-11-29 21:01:00 +0000463 fRadius = SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 return true;
465 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000466 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
robertphillips9f2251c2014-11-04 13:33:50 -0800467 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
469 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
470 if (SkScalarNearlyZero(sx - sy)) {
471 if (sx < 0) {
472 sx = -sx;
473 }
474
475 fMode = mode;
476 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700477 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000478 fRC = rc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
480 return true;
481 }
482 }
483 return false;
484}
485
reed@google.com045e62d2011-10-24 12:19:46 +0000486PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700487 Proc proc = nullptr;
reed@google.coma76de3d2011-01-13 18:30:42 +0000488
reed@google.com045e62d2011-10-24 12:19:46 +0000489 SkBlitter* blitter = *blitterPtr;
490 if (fRC->isBW()) {
491 fClip = &fRC->bwRgn();
492 } else {
493 fWrapper.init(*fRC, blitter);
494 fClip = &fWrapper.getRgn();
495 blitter = fWrapper.getBlitter();
496 *blitterPtr = blitter;
497 }
498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 // for our arrays
500 SkASSERT(0 == SkCanvas::kPoints_PointMode);
501 SkASSERT(1 == SkCanvas::kLines_PointMode);
502 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
503 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
504
reed@google.com2d47a212012-11-29 21:01:00 +0000505 if (fPaint->isAntiAlias()) {
506 if (0 == fPaint->getStrokeWidth()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 static const Proc gAAProcs[] = {
508 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
509 };
510 proc = gAAProcs[fMode];
reed@google.com2d47a212012-11-29 21:01:00 +0000511 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
512 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
513 proc = aa_square_proc;
514 }
515 } else { // BW
516 if (fRadius <= SK_FixedHalf) { // small radii and hairline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
518 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700519 const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
reed@google.com900ecf22014-02-20 20:55:37 +0000520 if (bm && kRGB_565_SkColorType == bm->colorType()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 proc = bw_pt_rect_16_hair_proc;
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000522 } else if (bm && kN32_SkColorType == bm->colorType()) {
reed@google.com2d47a212012-11-29 21:01:00 +0000523 proc = bw_pt_rect_32_hair_proc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 } else {
525 proc = bw_pt_rect_hair_proc;
526 }
527 } else {
528 static Proc gBWProcs[] = {
529 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
530 };
531 proc = gBWProcs[fMode];
532 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 } else {
534 proc = bw_square_proc;
535 }
536 }
537 return proc;
538}
539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540// each of these costs 8-bytes of stack space, so don't make it too large
541// must be even for lines/polygon to work
542#define MAX_DEV_PTS 32
543
544void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000545 const SkPoint pts[], const SkPaint& paint,
546 bool forceUseDevice) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 // if we're in lines mode, force count to be even
548 if (SkCanvas::kLines_PointMode == mode) {
549 count &= ~(size_t)1;
550 }
551
552 if ((long)count <= 0) {
553 return;
554 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000555
halcanary96fcdcc2015-08-27 07:41:13 -0700556 SkASSERT(pts != nullptr);
reed@android.comf2b98d62010-12-20 18:26:13 +0000557 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000558
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000560 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 return;
562 }
563
564 PtProcRec rec;
reed@google.com045e62d2011-10-24 12:19:46 +0000565 if (!forceUseDevice && rec.init(mode, paint, fMatrix, fRC)) {
reed41e010c2015-06-09 12:16:53 -0700566 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567
568 SkPoint devPts[MAX_DEV_PTS];
569 const SkMatrix* matrix = fMatrix;
570 SkBlitter* bltr = blitter.get();
reed@google.com045e62d2011-10-24 12:19:46 +0000571 PtProcRec::Proc proc = rec.chooseProc(&bltr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 // we have to back up subsequent passes if we're in polygon mode
573 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 do {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000576 int n = SkToInt(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 if (n > MAX_DEV_PTS) {
578 n = MAX_DEV_PTS;
579 }
580 matrix->mapPoints(devPts, pts, n);
581 proc(rec, devPts, n, bltr);
582 pts += n - backup;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000583 SkASSERT(SkToInt(count) >= n);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 count -= n;
585 if (count > 0) {
586 count += backup;
587 }
588 } while (count != 0);
589 } else {
590 switch (mode) {
591 case SkCanvas::kPoints_PointMode: {
592 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000593 SkPaint newPaint(paint);
594 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595
reed@google.com40c2ba22011-07-25 19:41:22 +0000596 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000598
reed@google.com40c2ba22011-07-25 19:41:22 +0000599 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 SkPath path;
601 SkMatrix preMatrix;
mtklein7ef849d2014-11-24 09:11:45 -0800602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 path.addCircle(0, 0, radius);
604 for (size_t i = 0; i < count; i++) {
605 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
606 // pass true for the last point, since we can modify
607 // then path then
jvanverthb3eb6872014-10-24 07:12:51 -0700608 path.setIsVolatile((count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000609 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000610 fDevice->drawPath(*this, path, newPaint, &preMatrix,
reed@android.comf2b98d62010-12-20 18:26:13 +0000611 (count-1) == i);
612 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000613 this->drawPath(path, newPaint, &preMatrix,
614 (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000615 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 }
617 } else {
618 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 for (size_t i = 0; i < count; i++) {
621 r.fLeft = pts[i].fX - radius;
622 r.fTop = pts[i].fY - radius;
623 r.fRight = r.fLeft + width;
624 r.fBottom = r.fTop + width;
reed@android.comf2b98d62010-12-20 18:26:13 +0000625 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000626 fDevice->drawRect(*this, r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000627 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000628 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 }
631 }
632 break;
633 }
634 case SkCanvas::kLines_PointMode:
bsalomon49f085d2014-09-05 13:34:00 -0700635 if (2 == count && paint.getPathEffect()) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000636 // most likely a dashed line - see if it is one of the ones
637 // we can accelerate
638 SkStrokeRec rec(paint);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000639 SkPathEffect::PointData pointData;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000640
641 SkPath path;
642 path.moveTo(pts[0]);
643 path.lineTo(pts[1]);
644
reed@google.com4bbdeac2013-01-24 21:03:11 +0000645 SkRect cullRect = SkRect::Make(fRC->getBounds());
skia.committer@gmail.com4024f322013-01-25 07:06:46 +0000646
reed@google.com4bbdeac2013-01-24 21:03:11 +0000647 if (paint.getPathEffect()->asPoints(&pointData, path, rec,
648 *fMatrix, &cullRect)) {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000649 // 'asPoints' managed to find some fast path
650
robertphillips@google.com629ab542012-11-28 17:18:11 +0000651 SkPaint newP(paint);
halcanary96fcdcc2015-08-27 07:41:13 -0700652 newP.setPathEffect(nullptr);
robertphillips@google.com935ad022012-12-05 19:07:21 +0000653 newP.setStyle(SkPaint::kFill_Style);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000654
robertphillips@google.com6d875572012-12-17 18:56:29 +0000655 if (!pointData.fFirst.isEmpty()) {
656 if (fDevice) {
657 fDevice->drawPath(*this, pointData.fFirst, newP);
658 } else {
659 this->drawPath(pointData.fFirst, newP);
660 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000661 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000662
663 if (!pointData.fLast.isEmpty()) {
664 if (fDevice) {
665 fDevice->drawPath(*this, pointData.fLast, newP);
666 } else {
667 this->drawPath(pointData.fLast, newP);
668 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000669 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000670
671 if (pointData.fSize.fX == pointData.fSize.fY) {
672 // The rest of the dashed line can just be drawn as points
673 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
674
675 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
676 newP.setStrokeCap(SkPaint::kRound_Cap);
677 } else {
678 newP.setStrokeCap(SkPaint::kButt_Cap);
679 }
680
681 if (fDevice) {
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000682 fDevice->drawPoints(*this,
robertphillips@google.com6d875572012-12-17 18:56:29 +0000683 SkCanvas::kPoints_PointMode,
684 pointData.fNumPoints,
685 pointData.fPoints,
686 newP);
687 } else {
688 this->drawPoints(SkCanvas::kPoints_PointMode,
689 pointData.fNumPoints,
690 pointData.fPoints,
691 newP,
692 forceUseDevice);
693 }
694 break;
695 } else {
696 // The rest of the dashed line must be drawn as rects
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000697 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
robertphillips@google.com6d875572012-12-17 18:56:29 +0000698 pointData.fFlags));
699
700 SkRect r;
701
702 for (int i = 0; i < pointData.fNumPoints; ++i) {
703 r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
704 pointData.fPoints[i].fY - pointData.fSize.fY,
705 pointData.fPoints[i].fX + pointData.fSize.fX,
706 pointData.fPoints[i].fY + pointData.fSize.fY);
707 if (fDevice) {
708 fDevice->drawRect(*this, r, newP);
709 } else {
710 this->drawRect(r, newP);
711 }
712 }
713 }
714
robertphillips@google.com629ab542012-11-28 17:18:11 +0000715 break;
716 }
717 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000718 // couldn't take fast path so fall through!
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 case SkCanvas::kPolygon_PointMode: {
720 count -= 1;
721 SkPath path;
722 SkPaint p(paint);
723 p.setStyle(SkPaint::kStroke_Style);
724 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
jvanverthb3eb6872014-10-24 07:12:51 -0700725 path.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 for (size_t i = 0; i < count; i += inc) {
727 path.moveTo(pts[i]);
728 path.lineTo(pts[i+1]);
reed@android.comf2b98d62010-12-20 18:26:13 +0000729 if (fDevice) {
halcanary96fcdcc2015-08-27 07:41:13 -0700730 fDevice->drawPath(*this, path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000731 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700732 this->drawPath(path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000733 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 path.rewind();
735 }
736 break;
737 }
738 }
739 }
740}
741
fmalita1a178ca2015-01-15 06:01:23 -0800742static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
743 SkASSERT(matrix.rectStaysRect());
744 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
745
746 SkVector size;
747 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
748 matrix.mapVectors(&size, &pt, 1);
749 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
750}
751
reed@google.com761fb622011-04-04 18:58:05 +0000752static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
753 SkPoint* strokeSize) {
754 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
755 paint.getStrokeMiter() < SK_ScalarSqrt2) {
756 return false;
757 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000758
fmalita1a178ca2015-01-15 06:01:23 -0800759 *strokeSize = compute_stroke_size(paint, matrix);
reed@google.com761fb622011-04-04 18:58:05 +0000760 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000761}
762
reed@google.com62ab7ad2011-04-05 14:08:25 +0000763SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
764 const SkMatrix& matrix,
765 SkPoint* strokeSize) {
766 RectType rtype;
767 const SkScalar width = paint.getStrokeWidth();
768 const bool zeroWidth = (0 == width);
769 SkPaint::Style style = paint.getStyle();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000770
reed@google.com62ab7ad2011-04-05 14:08:25 +0000771 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
772 style = SkPaint::kFill_Style;
773 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000774
reed@google.com62ab7ad2011-04-05 14:08:25 +0000775 if (paint.getPathEffect() || paint.getMaskFilter() ||
776 paint.getRasterizer() || !matrix.rectStaysRect() ||
777 SkPaint::kStrokeAndFill_Style == style) {
778 rtype = kPath_RectType;
779 } else if (SkPaint::kFill_Style == style) {
780 rtype = kFill_RectType;
781 } else if (zeroWidth) {
782 rtype = kHair_RectType;
783 } else if (easy_rect_join(paint, matrix, strokeSize)) {
784 rtype = kStroke_RectType;
785 } else {
786 rtype = kPath_RectType;
787 }
788 return rtype;
789}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000790
reed@google.com73244152012-05-11 14:35:23 +0000791static const SkPoint* rect_points(const SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000792 return SkTCast<const SkPoint*>(&r);
reed@google.com73244152012-05-11 14:35:23 +0000793}
794
795static SkPoint* rect_points(SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000796 return SkTCast<SkPoint*>(&r);
reed@google.com40c2ba22011-07-25 19:41:22 +0000797}
798
reed03939122014-12-15 13:42:51 -0800799void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
800 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000801 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802
803 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000804 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 return;
806 }
807
reed03939122014-12-15 13:42:51 -0800808 const SkMatrix* matrix;
809 SkMatrix combinedMatrixStorage;
810 if (paintMatrix) {
811 SkASSERT(postPaintRect);
812 combinedMatrixStorage.setConcat(*fMatrix, *paintMatrix);
813 matrix = &combinedMatrixStorage;
814 } else {
815 SkASSERT(!postPaintRect);
816 matrix = fMatrix;
817 }
818
reed@google.com761fb622011-04-04 18:58:05 +0000819 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000820 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000821
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000822 if (kPath_RectType == rtype) {
reed03939122014-12-15 13:42:51 -0800823 SkDraw draw(*this);
824 if (paintMatrix) {
825 draw.fMatrix = matrix;
826 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 SkPath tmp;
reed03939122014-12-15 13:42:51 -0800828 tmp.addRect(prePaintRect);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 tmp.setFillType(SkPath::kWinding_FillType);
halcanary96fcdcc2015-08-27 07:41:13 -0700830 draw.drawPath(tmp, paint, nullptr, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 return;
832 }
833
reed03939122014-12-15 13:42:51 -0800834 SkRect devRect;
fmalita1a178ca2015-01-15 06:01:23 -0800835 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
836 // skip the paintMatrix when transforming the rect by the CTM
837 fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
reed@google.com73244152012-05-11 14:35:23 +0000838 devRect.sort();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 // look for the quick exit, before we build a blitter
fmalita1a178ca2015-01-15 06:01:23 -0800841 SkRect bbox = devRect;
reed@google.com1c028bd2013-08-28 15:23:19 +0000842 if (paint.getStyle() != SkPaint::kFill_Style) {
843 // extra space for hairlines
georgeb3eba472014-09-09 11:33:57 -0700844 if (paint.getStrokeWidth() == 0) {
fmalita1a178ca2015-01-15 06:01:23 -0800845 bbox.outset(1, 1);
georgeb3eba472014-09-09 11:33:57 -0700846 } else {
fmalita1a178ca2015-01-15 06:01:23 -0800847 // For kStroke_RectType, strokeSize is already computed.
848 const SkPoint& ssize = (kStroke_RectType == rtype)
849 ? strokeSize
850 : compute_stroke_size(paint, *fMatrix);
851 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
georgeb3eba472014-09-09 11:33:57 -0700852 }
reed@google.com1c028bd2013-08-28 15:23:19 +0000853 }
fmalita1a178ca2015-01-15 06:01:23 -0800854
855 SkIRect ir = bbox.roundOut();
reed@google.com1c028bd2013-08-28 15:23:19 +0000856 if (fRC->quickReject(ir)) {
857 return;
rmistry@google.come09d6f42013-08-27 18:53:41 +0000858 }
859
reed41e010c2015-06-09 12:16:53 -0700860 SkDeviceLooper looper(fDst, *fRC, ir, paint.isAntiAlias());
reed@google.com1c028bd2013-08-28 15:23:19 +0000861 while (looper.next()) {
862 SkRect localDevRect;
863 looper.mapRect(&localDevRect, devRect);
864 SkMatrix localMatrix;
reed03939122014-12-15 13:42:51 -0800865 looper.mapMatrix(&localMatrix, *matrix);
rmistry@google.come09d6f42013-08-27 18:53:41 +0000866
reed41e010c2015-06-09 12:16:53 -0700867 SkAutoBlitterChoose blitterStorage(looper.getPixmap(), localMatrix, paint);
reed@google.com1c028bd2013-08-28 15:23:19 +0000868 const SkRasterClip& clip = looper.getRC();
869 SkBlitter* blitter = blitterStorage.get();
870
871 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
872 // case we are also hairline (if we've gotten to here), which devolves to
873 // effectively just kFill
874 switch (rtype) {
875 case kFill_RectType:
876 if (paint.isAntiAlias()) {
877 SkScan::AntiFillRect(localDevRect, clip, blitter);
878 } else {
879 SkScan::FillRect(localDevRect, clip, blitter);
880 }
881 break;
882 case kStroke_RectType:
883 if (paint.isAntiAlias()) {
884 SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
885 } else {
886 SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
887 }
888 break;
889 case kHair_RectType:
890 if (paint.isAntiAlias()) {
891 SkScan::AntiHairRect(localDevRect, clip, blitter);
892 } else {
893 SkScan::HairRect(localDevRect, clip, blitter);
894 }
895 break;
896 default:
897 SkDEBUGFAIL("bad rtype");
898 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 }
900}
901
902void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
903 if (srcM.fBounds.isEmpty()) {
904 return;
905 }
906
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000907 const SkMask* mask = &srcM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000909 SkMask dstM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 if (paint.getMaskFilter() &&
robertphillipse80eb922015-12-17 11:33:12 -0800911 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 mask = &dstM;
913 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000914 SkAutoMaskFreeImage ami(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915
reed41e010c2015-06-09 12:16:53 -0700916 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000917 SkBlitter* blitter = blitterChooser.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918
reed@google.com045e62d2011-10-24 12:19:46 +0000919 SkAAClipBlitterWrapper wrapper;
920 const SkRegion* clipRgn;
921
922 if (fRC->isBW()) {
923 clipRgn = &fRC->bwRgn();
924 } else {
925 wrapper.init(*fRC, blitter);
926 clipRgn = &wrapper.getRgn();
927 blitter = wrapper.getBlitter();
928 }
929 blitter->blitMaskRegion(*mask, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930}
931
reed@android.comebdeeb82009-09-03 21:45:49 +0000932static SkScalar fast_len(const SkVector& vec) {
933 SkScalar x = SkScalarAbs(vec.fX);
934 SkScalar y = SkScalarAbs(vec.fY);
935 if (x < y) {
936 SkTSwap(x, y);
937 }
938 return x + SkScalarHalf(y);
939}
940
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000941bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
942 SkScalar* coverage) {
943 SkASSERT(strokeWidth > 0);
944 // We need to try to fake a thick-stroke with a modulated hairline.
reed@google.comecadf992011-09-19 19:18:18 +0000945
tomhudson@google.com8d430182011-06-06 19:11:19 +0000946 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000947 return false;
948 }
reed@google.comecadf992011-09-19 19:18:18 +0000949
reed@android.comebdeeb82009-09-03 21:45:49 +0000950 SkVector src[2], dst[2];
reed@google.comecadf992011-09-19 19:18:18 +0000951 src[0].set(strokeWidth, 0);
952 src[1].set(0, strokeWidth);
reed@android.comebdeeb82009-09-03 21:45:49 +0000953 matrix.mapVectors(dst, src, 2);
954 SkScalar len0 = fast_len(dst[0]);
955 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000956 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
bsalomon49f085d2014-09-05 13:34:00 -0700957 if (coverage) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000958 *coverage = SkScalarAve(len0, len1);
959 }
reed@android.comebdeeb82009-09-03 21:45:49 +0000960 return true;
961 }
962 return false;
963}
964
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000965void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
966 SkDEBUGCODE(this->validate());
967
968 if (fRC->isEmpty()) {
969 return;
970 }
971
972 {
973 // TODO: Investigate optimizing these options. They are in the same
974 // order as SkDraw::drawPath, which handles each case. It may be
975 // that there is no way to optimize for these using the SkRRect path.
976 SkScalar coverage;
977 if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
978 goto DRAW_PATH;
979 }
980
981 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
982 goto DRAW_PATH;
983 }
984
985 if (paint.getRasterizer()) {
986 goto DRAW_PATH;
987 }
988 }
989
990 if (paint.getMaskFilter()) {
991 // Transform the rrect into device space.
992 SkRRect devRRect;
993 if (rrect.transform(*fMatrix, &devRRect)) {
reed41e010c2015-06-09 12:16:53 -0700994 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
bsalomon055e1922016-05-06 07:22:58 -0700995 if (paint.getMaskFilter()->filterRRect(devRRect, *fMatrix, *fRC, blitter.get())) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000996 return; // filterRRect() called the blitter, so we're done
997 }
998 }
999 }
1000
1001DRAW_PATH:
1002 // Now fall back to the default case of using a path.
1003 SkPath path;
1004 path.addRRect(rrect);
halcanary96fcdcc2015-08-27 07:41:13 -07001005 this->drawPath(path, paint, nullptr, true);
scroggo@google.coma8e33a92013-11-08 18:02:53 +00001006}
1007
caryclark1a7eb262016-01-21 07:07:02 -08001008SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
reed05d90442015-02-12 13:35:52 -08001009 if (!matrix.hasPerspective()) {
1010 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
1011 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
1012 if (SkScalarsAreFinite(sx, sy)) {
1013 return SkTMax(sx, sy);
1014 }
1015 }
1016 return 1;
1017}
1018
reed@google.com32e5d972011-07-27 18:25:57 +00001019void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
reed@google.com126f7f52013-11-07 16:06:53 +00001020 const SkMatrix* prePathMatrix, bool pathIsMutable,
krajcevski53f09592014-08-06 11:12:14 -07001021 bool drawCoverage, SkBlitter* customBlitter) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001022 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023
1024 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001025 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 return;
1027 }
1028
1029 SkPath* pathPtr = (SkPath*)&origSrcPath;
1030 bool doFill = true;
1031 SkPath tmpPath;
1032 SkMatrix tmpMatrix;
1033 const SkMatrix* matrix = fMatrix;
jvanverthb3eb6872014-10-24 07:12:51 -07001034 tmpPath.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035
1036 if (prePathMatrix) {
reed@google.com32e5d972011-07-27 18:25:57 +00001037 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
1038 origPaint.getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +00001040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 if (!pathIsMutable) {
1042 result = &tmpPath;
1043 pathIsMutable = true;
1044 }
1045 pathPtr->transform(*prePathMatrix, result);
1046 pathPtr = result;
1047 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001048 tmpMatrix.setConcat(*matrix, *prePathMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 matrix = &tmpMatrix;
1050 }
1051 }
1052 // at this point we're done with prePathMatrix
1053 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +00001054
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001055 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
reed@google.coma76de3d2011-01-13 18:30:42 +00001056
reed@google.comecadf992011-09-19 19:18:18 +00001057 {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001058 SkScalar coverage;
1059 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
1060 if (SK_Scalar1 == coverage) {
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001061 paint.writable()->setStrokeWidth(0);
egdanieldcfb7cf2015-01-22 06:52:29 -08001062 } else if (SkXfermode::SupportsCoverageAsAlpha(origPaint.getXfermode())) {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001063 U8CPU newAlpha;
1064#if 0
1065 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
1066 origPaint.getAlpha()));
1067#else
1068 // this is the old technique, which we preserve for now so
1069 // we don't change previous results (testing)
1070 // the new way seems fine, its just (a tiny bit) different
1071 int scale = (int)SkScalarMul(coverage, 256);
1072 newAlpha = origPaint.getAlpha() * scale >> 8;
1073#endif
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001074 SkPaint* writablePaint = paint.writable();
1075 writablePaint->setStrokeWidth(0);
1076 writablePaint->setAlpha(newAlpha);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001077 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 }
1079 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001080
reed@google.com32e5d972011-07-27 18:25:57 +00001081 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
reed@google.com4bbdeac2013-01-24 21:03:11 +00001082 SkRect cullRect;
halcanary96fcdcc2015-08-27 07:41:13 -07001083 const SkRect* cullRectPtr = nullptr;
reed@google.com4bbdeac2013-01-24 21:03:11 +00001084 if (this->computeConservativeLocalClipBounds(&cullRect)) {
1085 cullRectPtr = &cullRect;
1086 }
reed05d90442015-02-12 13:35:52 -08001087 doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr,
caryclark1a7eb262016-01-21 07:07:02 -08001088 ComputeResScaleForStroking(*fMatrix));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 pathPtr = &tmpPath;
1090 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001091
reed@google.com32e5d972011-07-27 18:25:57 +00001092 if (paint->getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 SkMask mask;
reed@google.com32e5d972011-07-27 18:25:57 +00001094 if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
reed@google.com045e62d2011-10-24 12:19:46 +00001095 &fRC->getBounds(), paint->getMaskFilter(), &mask,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
reed@google.com32e5d972011-07-27 18:25:57 +00001097 this->drawDevMask(mask, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 SkMask::FreeImage(mask.fImage);
1099 }
1100 return;
1101 }
1102
1103 // avoid possibly allocating a new path in transform if we can
1104 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1105
1106 // transform the path into device space
1107 pathPtr->transform(*matrix, devPathPtr);
1108
halcanary96fcdcc2015-08-27 07:41:13 -07001109 SkBlitter* blitter = nullptr;
krajcevski53f09592014-08-06 11:12:14 -07001110 SkAutoBlitterChoose blitterStorage;
halcanary96fcdcc2015-08-27 07:41:13 -07001111 if (nullptr == customBlitter) {
reed41e010c2015-06-09 12:16:53 -07001112 blitterStorage.choose(fDst, *fMatrix, *paint, drawCoverage);
krajcevski53f09592014-08-06 11:12:14 -07001113 blitter = blitterStorage.get();
1114 } else {
1115 blitter = customBlitter;
1116 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001118 if (paint->getMaskFilter()) {
bsalomon055e1922016-05-06 07:22:58 -07001119 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
1120 : SkStrokeRec::kHairline_InitStyle;
krajcevski53f09592014-08-06 11:12:14 -07001121 if (paint->getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fRC, blitter, style)) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00001122 return; // filterPath() called the blitter, so we're done
1123 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 }
1125
reed@google.com045e62d2011-10-24 12:19:46 +00001126 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 if (doFill) {
reed@google.com32e5d972011-07-27 18:25:57 +00001128 if (paint->isAntiAlias()) {
reed@google.com045e62d2011-10-24 12:19:46 +00001129 proc = SkScan::AntiFillPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 } else {
reed@google.com045e62d2011-10-24 12:19:46 +00001131 proc = SkScan::FillPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 }
1133 } else { // hairline
reed@google.com32e5d972011-07-27 18:25:57 +00001134 if (paint->isAntiAlias()) {
caryclark2028d7f2015-12-09 14:04:46 -08001135 switch (paint->getStrokeCap()) {
1136 case SkPaint::kButt_Cap:
1137 proc = SkScan::AntiHairPath;
1138 break;
1139 case SkPaint::kSquare_Cap:
1140 proc = SkScan::AntiHairSquarePath;
1141 break;
1142 case SkPaint::kRound_Cap:
1143 proc = SkScan::AntiHairRoundPath;
1144 break;
1145 default:
1146 proc SK_INIT_TO_AVOID_WARNING;
1147 SkDEBUGFAIL("unknown paint cap type");
1148 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 } else {
caryclark2028d7f2015-12-09 14:04:46 -08001150 switch (paint->getStrokeCap()) {
1151 case SkPaint::kButt_Cap:
1152 proc = SkScan::HairPath;
1153 break;
1154 case SkPaint::kSquare_Cap:
1155 proc = SkScan::HairSquarePath;
1156 break;
1157 case SkPaint::kRound_Cap:
1158 proc = SkScan::HairRoundPath;
1159 break;
1160 default:
1161 proc SK_INIT_TO_AVOID_WARNING;
1162 SkDEBUGFAIL("unknown paint cap type");
1163 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164 }
1165 }
krajcevski53f09592014-08-06 11:12:14 -07001166 proc(*devPathPtr, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167}
1168
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
1170 const SkPaint& paint) const {
reed@google.com900ecf22014-02-20 20:55:37 +00001171 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172
fmalitac7e211a2016-01-07 10:34:46 -08001173 if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) {
reed@google.come1ca7052013-12-17 19:22:07 +00001174 int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
1175 int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176
reed92fc2ae2015-05-22 08:06:21 -07001177 SkAutoPixmapUnlock result;
1178 if (!bitmap.requestLock(&result)) {
reed@google.coma641f3f2012-12-13 22:16:30 +00001179 return;
1180 }
reed92fc2ae2015-05-22 08:06:21 -07001181 const SkPixmap& pmap = result.pixmap();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 SkMask mask;
reed92fc2ae2015-05-22 08:06:21 -07001183 mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 mask.fFormat = SkMask::kA8_Format;
reed92fc2ae2015-05-22 08:06:21 -07001185 mask.fRowBytes = SkToU32(pmap.rowBytes());
1186 // fImage is typed as writable, but in this case it is used read-only
1187 mask.fImage = (uint8_t*)pmap.addr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001188
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 this->drawDevMask(mask, paint);
1190 } else { // need to xform the bitmap first
1191 SkRect r;
1192 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001193
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 r.set(0, 0,
1195 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1196 fMatrix->mapRect(&r);
1197 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001198
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 // set the mask's bounds to the transformed bitmap-bounds,
1200 // clipped to the actual device
1201 {
1202 SkIRect devBounds;
reed41e010c2015-06-09 12:16:53 -07001203 devBounds.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 // need intersect(l, t, r, b) on irect
1205 if (!mask.fBounds.intersect(devBounds)) {
1206 return;
1207 }
1208 }
reed@android.com543ed932009-04-24 12:43:40 +00001209
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 mask.fFormat = SkMask::kA8_Format;
1211 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001212 size_t size = mask.computeImageSize();
1213 if (0 == size) {
1214 // the mask is too big to allocated, draw nothing
1215 return;
1216 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217
1218 // allocate (and clear) our temp buffer to hold the transformed bitmap
scroggo565901d2015-12-10 10:44:13 -08001219 SkAutoTMalloc<uint8_t> storage(size);
1220 mask.fImage = storage.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001222
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1224 {
1225 SkBitmap device;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001226 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
1227 mask.fImage, mask.fRowBytes);
reed@google.coma76de3d2011-01-13 18:30:42 +00001228
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 SkCanvas c(device);
1230 // need the unclipped top/left for the translate
1231 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1232 -SkIntToScalar(mask.fBounds.fTop));
1233 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001234
1235 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001236 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001237 SkPaint tmpPaint;
1238 tmpPaint.setFlags(paint.getFlags());
reed@google.com40c2ba22011-07-25 19:41:22 +00001239 SkAutoBitmapShaderInstall install(bitmap, tmpPaint);
reed@android.com3469c762009-02-24 19:03:20 +00001240 SkRect rr;
1241 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1242 SkIntToScalar(bitmap.height()));
reed@google.com40c2ba22011-07-25 19:41:22 +00001243 c.drawRect(rr, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 }
1245 this->drawDevMask(mask, paint);
1246 }
1247}
1248
reed@google.com045e62d2011-10-24 12:19:46 +00001249static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 const SkRect& srcR) {
1251 SkRect dstR;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 m.mapRect(&dstR, srcR);
reedb07a94f2014-11-19 05:03:18 -08001253 return c.quickReject(dstR.roundOut());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254}
1255
reed@google.com045e62d2011-10-24 12:19:46 +00001256static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257 int width, int height) {
1258 SkRect r;
1259 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1260 return clipped_out(matrix, clip, r);
1261}
1262
reedc240e712015-05-23 12:26:41 -07001263static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
1264 return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
reed@google.com045e62d2011-10-24 12:19:46 +00001265}
1266
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed03939122014-12-15 13:42:51 -08001268 const SkRect* dstBounds, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001269 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270
1271 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001272 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001274 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 return;
1276 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001277
reed@google.com40c2ba22011-07-25 19:41:22 +00001278 SkPaint paint(origPaint);
1279 paint.setStyle(SkPaint::kFill_Style);
reed@google.coma76de3d2011-01-13 18:30:42 +00001280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 SkMatrix matrix;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001282 matrix.setConcat(*fMatrix, prematrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283
reed@google.com045e62d2011-10-24 12:19:46 +00001284 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 return;
1286 }
1287
fmalitac7e211a2016-01-07 10:34:46 -08001288 if (bitmap.colorType() != kAlpha_8_SkColorType
1289 && SkTreatAsSprite(matrix, bitmap.dimensions(), paint)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001290 //
1291 // It is safe to call lock pixels now, since we know the matrix is
1292 // (more or less) identity.
1293 //
reedc240e712015-05-23 12:26:41 -07001294 SkAutoPixmapUnlock unlocker;
1295 if (!bitmap.requestLock(&unlocker)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001296 return;
1297 }
reedc240e712015-05-23 12:26:41 -07001298 const SkPixmap& pmap = unlocker.pixmap();
reed@google.come1ca7052013-12-17 19:22:07 +00001299 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1300 int iy = SkScalarRoundToInt(matrix.getTranslateY());
reedc240e712015-05-23 12:26:41 -07001301 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001302 SkTBlitterAllocator allocator;
1303 // blitter will be owned by the allocator.
reed41e010c2015-06-09 12:16:53 -07001304 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, ix, iy, &allocator);
reed@google.com045e62d2011-10-24 12:19:46 +00001305 if (blitter) {
reedc240e712015-05-23 12:26:41 -07001306 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
1307 *fRC, blitter);
reed@google.com045e62d2011-10-24 12:19:46 +00001308 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 }
reedc240e712015-05-23 12:26:41 -07001310 // if !blitter, then we fall-through to the slower case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001313
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 // now make a temp draw on the stack, and use it
1315 //
1316 SkDraw draw(*this);
1317 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001318
reed@google.com900ecf22014-02-20 20:55:37 +00001319 if (bitmap.colorType() == kAlpha_8_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 draw.drawBitmapAsMask(bitmap, paint);
1321 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +00001322 SkAutoBitmapShaderInstall install(bitmap, paint);
reed03939122014-12-15 13:42:51 -08001323 const SkPaint& paintWithShader = install.paintWithShader();
1324 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1325 if (dstBounds) {
1326 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1327 } else {
1328 draw.drawRect(srcBounds, paintWithShader);
1329 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 }
1331}
1332
reedc240e712015-05-23 12:26:41 -07001333void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001334 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001335
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001337 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001339 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 return;
1341 }
1342
reedc240e712015-05-23 12:26:41 -07001343 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344
reed@google.com045e62d2011-10-24 12:19:46 +00001345 if (fRC->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 return; // nothing to draw
1347 }
1348
reed@google.com40c2ba22011-07-25 19:41:22 +00001349 SkPaint paint(origPaint);
1350 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351
reedc240e712015-05-23 12:26:41 -07001352 SkAutoPixmapUnlock unlocker;
1353 if (!bitmap.requestLock(&unlocker)) {
1354 return;
1355 }
1356 const SkPixmap& pmap = unlocker.pixmap();
1357
halcanary96fcdcc2015-08-27 07:41:13 -07001358 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001359 SkTBlitterAllocator allocator;
1360 // blitter will be owned by the allocator.
reed41e010c2015-06-09 12:16:53 -07001361 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 if (blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +00001363 SkScan::FillIRect(bounds, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 return;
1365 }
1366 }
1367
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 SkMatrix matrix;
1369 SkRect r;
1370
1371 // get a scalar version of our rect
1372 r.set(bounds);
1373
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001374 // create shader with offset
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 matrix.setTranslate(r.fLeft, r.fTop);
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001376 SkAutoBitmapShaderInstall install(bitmap, paint, &matrix);
1377 const SkPaint& shaderPaint = install.paintWithShader();
reed@google.coma76de3d2011-01-13 18:30:42 +00001378
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 SkDraw draw(*this);
1380 matrix.reset();
1381 draw.fMatrix = &matrix;
1382 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001383 // is this OK if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001384 draw.drawRect(r, shaderPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385}
1386
1387///////////////////////////////////////////////////////////////////////////////
1388
1389#include "SkScalerContext.h"
1390#include "SkGlyphCache.h"
reed@google.come6913762012-08-07 15:19:47 +00001391#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392#include "SkUtils.h"
1393
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001394bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
1395 // hairline glyphs are fast enough so we don't need to cache them
1396 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
1397 return true;
1398 }
1399
1400 // we don't cache perspective
1401 if (ctm.hasPerspective()) {
1402 return true;
1403 }
1404
1405 SkMatrix textM;
1406 return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM));
1407}
1408
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
1410 SkScalar x, SkScalar y,
1411 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001412 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413
djsollen@google.com166e6532012-03-20 14:24:38 +00001414 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415
1416 SkMatrix matrix;
1417 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1418 matrix.postTranslate(x, y);
1419
1420 const SkPath* iterPath;
1421 SkScalar xpos, prevXPos = 0;
1422
reed@google.com7b4531f2012-08-07 15:53:00 +00001423 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001425 if (iterPath) {
1426 const SkPaint& pnt = iter.getPaint();
1427 if (fDevice) {
1428 fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
1429 } else {
1430 this->drawPath(*iterPath, pnt, &matrix, false);
1431 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001432 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 prevXPos = xpos;
1434 }
1435}
1436
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437// disable warning : local variable used without having been initialized
bungemand7dc76f2016-03-10 11:14:40 -08001438#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439#pragma warning ( push )
1440#pragma warning ( disable : 4701 )
1441#endif
1442
herbf553e4e2015-11-09 08:51:56 -08001443////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444
herbd4c24f62015-12-07 12:12:29 -08001445class DrawOneGlyph {
1446public:
1447 DrawOneGlyph(const SkDraw& draw, const SkPaint& paint, SkGlyphCache* cache, SkBlitter* blitter)
1448 : fUseRegionToDraw(UsingRegionToDraw(draw.fRC))
1449 , fGlyphCache(cache)
1450 , fBlitter(blitter)
1451 , fClip(fUseRegionToDraw ? &draw.fRC->bwRgn() : nullptr)
1452 , fDraw(draw)
1453 , fPaint(paint)
1454 , fClipBounds(PickClipBounds(draw)) { }
herb11a7f7f2015-11-24 12:41:00 -08001455
herbd4c24f62015-12-07 12:12:29 -08001456 void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
1457 position += rounding;
herbd4c24f62015-12-07 12:12:29 -08001458 // Prevent glyphs from being drawn outside of or straddling the edge of device space.
benjaminwagner77a9cc12016-03-25 11:24:30 -07001459 if (position.fX > INT_MAX - (INT16_MAX + UINT16_MAX) ||
1460 position.fX < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) ||
1461 position.fY > INT_MAX - (INT16_MAX + UINT16_MAX) ||
1462 position.fY < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) {
herbd4c24f62015-12-07 12:12:29 -08001463 return;
1464 }
herb11a7f7f2015-11-24 12:41:00 -08001465
benjaminwagner77a9cc12016-03-25 11:24:30 -07001466 int left = SkScalarFloorToInt(position.fX);
1467 int top = SkScalarFloorToInt(position.fY);
herbd4c24f62015-12-07 12:12:29 -08001468 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1469
1470 left += glyph.fLeft;
1471 top += glyph.fTop;
1472
1473 int right = left + glyph.fWidth;
1474 int bottom = top + glyph.fHeight;
1475
1476 SkMask mask;
1477 mask.fBounds.set(left, top, right, bottom);
1478
1479 if (fUseRegionToDraw) {
1480 SkRegion::Cliperator clipper(*fClip, mask.fBounds);
1481
1482 if (!clipper.done() && this->getImageData(glyph, &mask)) {
1483 const SkIRect& cr = clipper.rect();
1484 do {
1485 this->blitMask(mask, cr);
1486 clipper.next();
1487 } while (!clipper.done());
1488 }
1489 } else {
1490 SkIRect storage;
1491 SkIRect* bounds = &mask.fBounds;
1492
1493 // this extra test is worth it, assuming that most of the time it succeeds
1494 // since we can avoid writing to storage
1495 if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) {
1496 if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds))
1497 return;
1498 bounds = &storage;
1499 }
1500
1501 if (this->getImageData(glyph, &mask)) {
1502 this->blitMask(mask, *bounds);
1503 }
1504 }
1505 }
1506
1507private:
1508 static bool UsingRegionToDraw(const SkRasterClip* rClip) {
1509 return rClip->isBW() && !rClip->isRect();
1510 }
1511
1512 static SkIRect PickClipBounds(const SkDraw& draw) {
1513 const SkRasterClip& rasterClip = *draw.fRC;
1514
1515 if (rasterClip.isBW()) {
1516 return rasterClip.bwRgn().getBounds();
1517 } else {
1518 return rasterClip.aaRgn().getBounds();
1519 }
1520 }
1521
1522 bool getImageData(const SkGlyph& glyph, SkMask* mask) {
1523 uint8_t* bits = (uint8_t*)(fGlyphCache->findImage(glyph));
1524 if (nullptr == bits) {
1525 return false; // can't rasterize glyph
1526 }
1527 mask->fImage = bits;
1528 mask->fRowBytes = glyph.rowBytes();
1529 mask->fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1530 return true;
1531 }
1532
herb11a7f7f2015-11-24 12:41:00 -08001533 void blitMask(const SkMask& mask, const SkIRect& clip) const {
1534 if (SkMask::kARGB32_Format == mask.fFormat) {
herbd4c24f62015-12-07 12:12:29 -08001535 SkBitmap bm;
1536 bm.installPixels(
1537 SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()),
1538 (SkPMColor*)mask.fImage, mask.fRowBytes);
1539
1540 fDraw.drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), fPaint);
herb11a7f7f2015-11-24 12:41:00 -08001541 } else {
1542 fBlitter->blitMask(mask, clip);
1543 }
1544 }
1545
herbd4c24f62015-12-07 12:12:29 -08001546 const bool fUseRegionToDraw;
1547 SkGlyphCache * const fGlyphCache;
1548 SkBlitter * const fBlitter;
1549 const SkRegion* const fClip;
1550 const SkDraw& fDraw;
1551 const SkPaint& fPaint;
1552 const SkIRect fClipBounds;
herb11a7f7f2015-11-24 12:41:00 -08001553};
1554
herbd4c24f62015-12-07 12:12:29 -08001555////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556
brianosmana1e8f8d2016-04-08 06:47:54 -07001557uint32_t SkDraw::scalerContextFlags() const {
1558 uint32_t flags = SkPaint::kBoostContrast_ScalerContextFlag;
1559 if (fDevice->imageInfo().isLinear()) {
1560 flags |= SkPaint::kFakeGamma_ScalerContextFlag;
1561 }
1562 return flags;
bungemanf6d1e602016-02-22 13:20:28 -08001563}
1564
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565void SkDraw::drawText(const char text[], size_t byteLength,
1566 SkScalar x, SkScalar y, const SkPaint& paint) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001567 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568
reed@android.comf2b98d62010-12-20 18:26:13 +00001569 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570
1571 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001572 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 return;
1574 }
1575
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001576 // SkScalarRec doesn't currently have a way of representing hairline stroke and
1577 // will fill if its frame-width is 0.
reed@google.comed43dff2013-06-04 16:56:27 +00001578 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 this->drawText_asPaths(text, byteLength, x, y, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580 return;
1581 }
1582
brianosmana1e8f8d2016-04-08 06:47:54 -07001583 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), fMatrix);
herb11a7f7f2015-11-24 12:41:00 -08001584
1585 // The Blitter Choose needs to be live while using the blitter below.
1586 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1587 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001588 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589
herbe59124e2015-11-18 10:54:39 -08001590 SkFindAndPlaceGlyph::ProcessText(
herb4c11b3f2015-11-20 13:53:12 -08001591 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001592 {x, y}, *fMatrix, paint.getTextAlign(), cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593}
1594
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595//////////////////////////////////////////////////////////////////////////////
1596
reed@google.comed43dff2013-06-04 16:56:27 +00001597void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -07001598 const SkScalar pos[], int scalarsPerPosition,
1599 const SkPoint& offset, const SkPaint& origPaint) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001600 // setup our std paint, in hopes of getting hits in the cache
1601 SkPaint paint(origPaint);
1602 SkScalar matrixScale = paint.setupForAsPaths();
1603
reed@google.com5a649022013-06-05 18:00:30 +00001604 SkMatrix matrix;
1605 matrix.setScale(matrixScale, matrixScale);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001606
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001607 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
1608 paint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07001609 paint.setPathEffect(nullptr);
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001610
benjaminwagnerd936f632016-02-23 10:44:31 -08001611 SkPaint::GlyphCacheProc glyphCacheProc = paint.getGlyphCacheProc(true);
brianosmana1e8f8d2016-04-08 06:47:54 -07001612 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), nullptr);
reed@google.comed43dff2013-06-04 16:56:27 +00001613
1614 const char* stop = text + byteLength;
bungeman79738cc2015-03-11 14:05:29 -07001615 SkTextAlignProc alignProc(paint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -07001616 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001617
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001618 // Now restore the original settings, so we "draw" with whatever style/stroking.
1619 paint.setStyle(origPaint.getStyle());
reeda4393342016-03-18 11:22:57 -07001620 paint.setPathEffect(sk_ref_sp(origPaint.getPathEffect()));
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001621
reed@google.comed43dff2013-06-04 16:56:27 +00001622 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -08001623 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
reed@google.comed43dff2013-06-04 16:56:27 +00001624 if (glyph.fWidth) {
1625 const SkPath* path = cache->findPath(glyph);
1626 if (path) {
kkinnunencb9a2c82014-06-12 23:06:28 -07001627 SkPoint tmsLoc;
1628 tmsProc(pos, &tmsLoc);
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001629 SkPoint loc;
kkinnunencb9a2c82014-06-12 23:06:28 -07001630 alignProc(tmsLoc, glyph, &loc);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001631
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001632 matrix[SkMatrix::kMTransX] = loc.fX;
1633 matrix[SkMatrix::kMTransY] = loc.fY;
reed@google.com5a649022013-06-05 18:00:30 +00001634 if (fDevice) {
1635 fDevice->drawPath(*this, *path, paint, &matrix, false);
1636 } else {
1637 this->drawPath(*path, paint, &matrix, false);
1638 }
reed@google.comed43dff2013-06-04 16:56:27 +00001639 }
1640 }
1641 pos += scalarsPerPosition;
1642 }
1643}
1644
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645void SkDraw::drawPosText(const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -07001646 const SkScalar pos[], int scalarsPerPosition,
1647 const SkPoint& offset, const SkPaint& paint) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001648 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1650
reed@android.comf2b98d62010-12-20 18:26:13 +00001651 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652
1653 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001654 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 return;
1656 }
1657
reed@google.comed43dff2013-06-04 16:56:27 +00001658 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
fmalita05c4a432014-09-29 06:29:53 -07001659 this->drawPosText_asPaths(text, byteLength, pos, scalarsPerPosition, offset, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660 return;
1661 }
1662
brianosmana1e8f8d2016-04-08 06:47:54 -07001663 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), fMatrix);
herbd4c24f62015-12-07 12:12:29 -08001664
herb9be5ff62015-11-11 11:30:11 -08001665 // The Blitter Choose needs to be live while using the blitter below.
herb11a7f7f2015-11-24 12:41:00 -08001666 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1667 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001668 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
herbd4c24f62015-12-07 12:12:29 -08001669 SkPaint::Align textAlignment = paint.getTextAlign();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670
herb9be5ff62015-11-11 11:30:11 -08001671 SkFindAndPlaceGlyph::ProcessPosText(
herb4c11b3f2015-11-20 13:53:12 -08001672 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001673 offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674}
1675
bungemand7dc76f2016-03-10 11:14:40 -08001676#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677#pragma warning ( pop )
1678#endif
1679
1680///////////////////////////////////////////////////////////////////////////////
1681
reed5dc6b7d2015-04-14 10:40:44 -07001682static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
1684}
1685
1686static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
1687 const SkPoint texs[], SkMatrix* matrix) {
1688 SkPoint src[3], dst[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00001689
reed@android.com8a1c16f2008-12-17 15:59:43 +00001690 src[0] = texs[state.f0];
1691 src[1] = texs[state.f1];
1692 src[2] = texs[state.f2];
1693 dst[0] = verts[state.f0];
1694 dst[1] = verts[state.f1];
1695 dst[2] = verts[state.f2];
1696 return matrix->setPolyToPoly(src, dst, 3);
1697}
1698
1699class SkTriColorShader : public SkShader {
1700public:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001701 SkTriColorShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001703 class TriColorShaderContext : public SkShader::Context {
1704 public:
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001705 TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001706 virtual ~TriColorShaderContext();
mtklein36352bf2015-03-25 18:17:31 -07001707 void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001708
1709 private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001710 bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
1711
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001712 SkMatrix fDstToUnit;
1713 SkPMColor fColors[3];
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001714 bool fSetup;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001715
1716 typedef SkShader::Context INHERITED;
1717 };
reed@google.coma76de3d2011-01-13 18:30:42 +00001718
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001719 struct TriColorShaderData {
1720 const SkPoint* pts;
1721 const SkColor* colors;
1722 const VertState *state;
1723 };
1724
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001725 SK_TO_STRING_OVERRIDE()
mtklein7e44bb12015-01-07 09:06:08 -08001726
1727 // For serialization. This will never be called.
halcanary96fcdcc2015-08-27 07:41:13 -07001728 Factory getFactory() const override { sk_throw(); return nullptr; }
djsollen@google.comba28d032012-03-26 17:57:35 +00001729
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001730 // Supply setup data to context from drawing setup
1731 void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; }
1732
1733 // Take the setup data from context when needed.
1734 TriColorShaderData* takeSetupData() {
1735 TriColorShaderData *data = fSetupData;
1736 fSetupData = NULL;
1737 return data;
1738 }
1739
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740protected:
reed773ceda2016-03-03 18:18:25 -08001741 size_t onContextSize(const ContextRec&) const override;
mtklein36352bf2015-03-25 18:17:31 -07001742 Context* onCreateContext(const ContextRec& rec, void* storage) const override {
halcanary385fe4d2015-08-26 13:07:48 -07001743 return new (storage) TriColorShaderContext(*this, rec);
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +00001744 }
1745
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001747 TriColorShaderData *fSetupData;
1748
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749 typedef SkShader INHERITED;
1750};
1751
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001752bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[],
1753 int index0, int index1, int index2) {
reed@google.coma76de3d2011-01-13 18:30:42 +00001754
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755 fColors[0] = SkPreMultiplyColor(colors[index0]);
1756 fColors[1] = SkPreMultiplyColor(colors[index1]);
1757 fColors[2] = SkPreMultiplyColor(colors[index2]);
reed@google.coma76de3d2011-01-13 18:30:42 +00001758
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759 SkMatrix m, im;
1760 m.reset();
1761 m.set(0, pts[index1].fX - pts[index0].fX);
1762 m.set(1, pts[index2].fX - pts[index0].fX);
1763 m.set(2, pts[index0].fX);
1764 m.set(3, pts[index1].fY - pts[index0].fY);
1765 m.set(4, pts[index2].fY - pts[index0].fY);
1766 m.set(5, pts[index0].fY);
1767 if (!m.invert(&im)) {
1768 return false;
1769 }
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001770 // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix
1771 // as our interators are intrinsically tied to the vertices, and nothing else.
1772 SkMatrix ctmInv;
1773 if (!this->getCTM().invert(&ctmInv)) {
1774 return false;
1775 }
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001776 // TODO replace INV(m) * INV(ctm) with INV(ctm * m)
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001777 fDstToUnit.setConcat(im, ctmInv);
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001778 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779}
1780
1781#include "SkColorPriv.h"
reed@android.com689411a2008-12-18 02:52:32 +00001782#include "SkComposeShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783
1784static int ScalarTo256(SkScalar v) {
benjaminwagner43437c22016-02-24 10:57:47 -08001785 return static_cast<int>(SkScalarPin(v, 0, 1) * 256 + 0.5);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001788SkTriColorShader::SkTriColorShader()
1789 : INHERITED(NULL)
1790 , fSetupData(NULL) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001791
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001792SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader,
1793 const ContextRec& rec)
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001794 : INHERITED(shader, rec)
1795 , fSetup(false) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001796
1797SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {}
1798
reed773ceda2016-03-03 18:18:25 -08001799size_t SkTriColorShader::onContextSize(const ContextRec&) const {
reeda0cee5f2016-03-04 07:38:11 -08001800 return sizeof(TriColorShaderContext);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001801}
reed773ceda2016-03-03 18:18:25 -08001802
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001803void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001804 SkTriColorShader* parent = static_cast<SkTriColorShader*>(const_cast<SkShader*>(&fShader));
1805 TriColorShaderData* set = parent->takeSetupData();
1806 if (set) {
1807 fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2);
1808 }
1809
1810 if (!fSetup) {
1811 // Invalid matrices. Not checked before so no need to assert.
1812 return;
1813 }
1814
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001815 const int alphaScale = Sk255To256(this->getPaintAlpha());
1816
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 SkPoint src;
reed@google.coma76de3d2011-01-13 18:30:42 +00001818
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819 for (int i = 0; i < count; i++) {
1820 fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
1821 x += 1;
reed@google.coma76de3d2011-01-13 18:30:42 +00001822
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 int scale1 = ScalarTo256(src.fX);
1824 int scale2 = ScalarTo256(src.fY);
1825 int scale0 = 256 - scale1 - scale2;
1826 if (scale0 < 0) {
1827 if (scale1 > scale2) {
1828 scale2 = 256 - scale1;
1829 } else {
1830 scale1 = 256 - scale2;
1831 }
1832 scale0 = 0;
1833 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001834
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001835 if (256 != alphaScale) {
1836 scale0 = SkAlphaMul(scale0, alphaScale);
1837 scale1 = SkAlphaMul(scale1, alphaScale);
1838 scale2 = SkAlphaMul(scale2, alphaScale);
1839 }
1840
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841 dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001842 SkAlphaMulQ(fColors[1], scale1) +
1843 SkAlphaMulQ(fColors[2], scale2);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844 }
1845}
1846
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001847#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001848void SkTriColorShader::toString(SkString* str) const {
1849 str->append("SkTriColorShader: (");
1850
1851 this->INHERITED::toString(str);
1852
1853 str->append(")");
1854}
1855#endif
1856
reed@android.com8a1c16f2008-12-17 15:59:43 +00001857void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
1858 const SkPoint vertices[], const SkPoint textures[],
1859 const SkColor colors[], SkXfermode* xmode,
1860 const uint16_t indices[], int indexCount,
1861 const SkPaint& paint) const {
bsalomon49f085d2014-09-05 13:34:00 -07001862 SkASSERT(0 == count || vertices);
reed@google.coma76de3d2011-01-13 18:30:42 +00001863
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864 // abort early if there is nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001865 if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866 return;
1867 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001868
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 // transform out vertices into device coordinates
1870 SkAutoSTMalloc<16, SkPoint> storage(count);
1871 SkPoint* devVerts = storage.get();
1872 fMatrix->mapPoints(devVerts, vertices, count);
reed@google.coma76de3d2011-01-13 18:30:42 +00001873
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874 /*
1875 We can draw the vertices in 1 of 4 ways:
1876
1877 - solid color (no shader/texture[], no colors[])
1878 - just colors (no shader/texture[], has colors[])
1879 - just texture (has shader/texture[], no colors[])
1880 - colors * texture (has shader/texture[], has colors[])
reed@google.coma76de3d2011-01-13 18:30:42 +00001881
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 Thus for texture drawing, we need both texture[] and a shader.
1883 */
1884
reed8a21c9f2016-03-08 18:50:00 -08001885 auto triShader = sk_make_sp<SkTriColorShader>();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886 SkPaint p(paint);
1887
1888 SkShader* shader = p.getShader();
halcanary96fcdcc2015-08-27 07:41:13 -07001889 if (nullptr == shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 // if we have no shader, we ignore the texture coordinates
halcanary96fcdcc2015-08-27 07:41:13 -07001891 textures = nullptr;
1892 } else if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 // if we don't have texture coordinates, ignore the shader
halcanary96fcdcc2015-08-27 07:41:13 -07001894 p.setShader(nullptr);
1895 shader = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896 }
1897
1898 // setup the custom shader (if needed)
bsalomon49f085d2014-09-05 13:34:00 -07001899 if (colors) {
halcanary96fcdcc2015-08-27 07:41:13 -07001900 if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901 // just colors (no texture)
reed8a21c9f2016-03-08 18:50:00 -08001902 p.setShader(triShader);
1903 shader = p.getShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904 } else {
1905 // colors * texture
1906 SkASSERT(shader);
reedcfb6bdf2016-03-29 11:32:50 -07001907 sk_sp<SkXfermode> xfer = xmode ? sk_ref_sp(xmode)
1908 : SkXfermode::Make(SkXfermode::kModulate_Mode);
1909 p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), std::move(xfer)));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910 }
1911 }
1912
reed41e010c2015-06-09 12:16:53 -07001913 SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001914 // Abort early if we failed to create a shader context.
reed@google.comea033602012-12-14 13:13:55 +00001915 if (blitter->isNullBlitter()) {
1916 return;
1917 }
1918
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919 // setup our state and function pointer for iterating triangles
1920 VertState state(count, indices, indexCount);
1921 VertState::Proc vertProc = state.chooseProc(vmode);
reed@google.coma76de3d2011-01-13 18:30:42 +00001922
bsalomon49f085d2014-09-05 13:34:00 -07001923 if (textures || colors) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001924 SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state };
1925
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 while (vertProc(&state)) {
bsalomon49f085d2014-09-05 13:34:00 -07001927 if (textures) {
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001928 SkMatrix tempM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929 if (texture_to_matrix(state, vertices, textures, &tempM)) {
fmalitad0c4e092016-02-22 17:19:04 -08001930 SkShader::ContextRec rec(p, *fMatrix, &tempM,
1931 SkBlitter::PreferredShaderDest(fDst.info()));
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001932 if (!blitter->resetShaderContext(rec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 continue;
1934 }
1935 }
1936 }
bsalomon49f085d2014-09-05 13:34:00 -07001937 if (colors) {
reed8a21c9f2016-03-08 18:50:00 -08001938 triShader->bindSetupData(&verticesSetup);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001939 }
reed@google.com045e62d2011-10-24 12:19:46 +00001940
1941 SkPoint tmp[] = {
1942 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
1943 };
1944 SkScan::FillTriangle(tmp, *fRC, blitter.get());
reed8a21c9f2016-03-08 18:50:00 -08001945 triShader->bindSetupData(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 } else {
commit-bot@chromium.org559a8832014-05-30 10:08:22 +00001948 // no colors[] and no texture, stroke hairlines with paint's color.
reed5dc6b7d2015-04-14 10:40:44 -07001949 SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
reed@google.com045e62d2011-10-24 12:19:46 +00001950 const SkRasterClip& clip = *fRC;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 while (vertProc(&state)) {
reed5dc6b7d2015-04-14 10:40:44 -07001952 SkPoint array[] = {
1953 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
1954 };
1955 hairProc(array, 4, clip, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956 }
1957 }
1958}
1959
reed@google.com0a0a2362011-03-23 13:51:55 +00001960///////////////////////////////////////////////////////////////////////////////
1961///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962
1963#ifdef SK_DEBUG
1964
reed@android.comf2b98d62010-12-20 18:26:13 +00001965void SkDraw::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -07001966 SkASSERT(fMatrix != nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -07001967 SkASSERT(fRC != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968
reed@google.com045e62d2011-10-24 12:19:46 +00001969 const SkIRect& cr = fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001970 SkIRect br;
1971
reed41e010c2015-06-09 12:16:53 -07001972 br.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973 SkASSERT(cr.isEmpty() || br.contains(cr));
1974}
1975
1976#endif
1977
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978////////////////////////////////////////////////////////////////////////////////////////////////
1979
1980#include "SkPath.h"
1981#include "SkDraw.h"
1982#include "SkRegion.h"
1983#include "SkBlitter.h"
1984
1985static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
reedb07a94f2014-11-19 05:03:18 -08001986 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987 SkIRect* bounds) {
1988 if (devPath.isEmpty()) {
1989 return false;
1990 }
1991
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992 // init our bounds from the path
reed11fa2242015-03-13 06:08:28 -07001993 *bounds = devPath.getBounds().makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
reed@google.coma76de3d2011-01-13 18:30:42 +00001994
tomhudson@google.com6db75fc2012-03-23 14:46:48 +00001995 SkIPoint margin = SkIPoint::Make(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 if (filter) {
1997 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00001998
bungeman@google.com5af16f82011-09-02 15:06:44 +00001999 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00002000
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001 srcM.fBounds = *bounds;
2002 srcM.fFormat = SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
2004 return false;
2005 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006 }
2007
bungeman@google.com5af16f82011-09-02 15:06:44 +00002008 // (possibly) trim the bounds to reflect the clip
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009 // (plus whatever slop the filter needs)
bungeman@google.com5af16f82011-09-02 15:06:44 +00002010 if (clipBounds) {
reed@android.com35555912009-03-16 18:46:55 +00002011 // Ugh. Guard against gigantic margins from wacky filters. Without this
2012 // check we can request arbitrary amounts of slop beyond our visible
2013 // clip, and bring down the renderer (at least on finite RAM machines
2014 // like handsets, etc.). Need to balance this invented value between
2015 // quality of large filters like blurs, and the corresponding memory
2016 // requests.
2017 static const int MAX_MARGIN = 128;
reed11fa2242015-03-13 06:08:28 -07002018 if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
2019 SkMin32(margin.fY, MAX_MARGIN)))) {
bungeman@google.com5af16f82011-09-02 15:06:44 +00002020 return false;
2021 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022 }
2023
2024 return true;
2025}
2026
bsalomon055e1922016-05-06 07:22:58 -07002027static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
2028 SkStrokeRec::InitStyle style) {
reed41e010c2015-06-09 12:16:53 -07002029 SkDraw draw;
2030 if (!draw.fDst.reset(mask)) {
2031 return;
2032 }
2033
reed@google.com045e62d2011-10-24 12:19:46 +00002034 SkRasterClip clip;
2035 SkMatrix matrix;
2036 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037
reed@google.com045e62d2011-10-24 12:19:46 +00002038 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
2040 -SkIntToScalar(mask.fBounds.fTop));
2041
reed@google.com045e62d2011-10-24 12:19:46 +00002042 draw.fRC = &clip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043 draw.fMatrix = &matrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 paint.setAntiAlias(true);
bsalomon055e1922016-05-06 07:22:58 -07002045 switch (style) {
2046 case SkStrokeRec::kHairline_InitStyle:
2047 SkASSERT(!paint.getStrokeWidth());
2048 paint.setStyle(SkPaint::kStroke_Style);
2049 break;
2050 case SkStrokeRec::kFill_InitStyle:
2051 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
2052 break;
2053
2054 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055 draw.drawPath(devPath, paint);
2056}
2057
2058bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +00002059 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002060 SkMask* mask, SkMask::CreateMode mode,
bsalomon055e1922016-05-06 07:22:58 -07002061 SkStrokeRec::InitStyle style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062 if (SkMask::kJustRenderImage_CreateMode != mode) {
2063 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
2064 return false;
2065 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002066
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
2068 mask->fFormat = SkMask::kA8_Format;
2069 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00002070 size_t size = mask->computeImageSize();
2071 if (0 == size) {
2072 // we're too big to allocate the mask, abort
2073 return false;
2074 }
2075 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076 memset(mask->fImage, 0, mask->computeImageSize());
2077 }
2078
2079 if (SkMask::kJustComputeBounds_CreateMode != mode) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002080 draw_into_mask(*mask, devPath, style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002082
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083 return true;
2084}