blob: f9f9a28aeb195b8678d80b3a30175f420a226b1d [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"
reed374772b2016-10-05 17:33:02 -070010#include "SkBlendModePriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkBlitter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkCanvas.h"
13#include "SkColorPriv.h"
14#include "SkDevice.h"
reed@google.com1c028bd2013-08-28 15:23:19 +000015#include "SkDeviceLooper.h"
herbe5911c92015-11-09 13:15:21 -080016#include "SkFindAndPlaceGlyph.h"
bungeman@google.com2211b622012-01-13 15:02:58 +000017#include "SkFixed.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkMaskFilter.h"
herbf553e4e2015-11-09 08:51:56 -080019#include "SkMatrix.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkPaint.h"
21#include "SkPathEffect.h"
reed@google.com045e62d2011-10-24 12:19:46 +000022#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkRasterizer.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000024#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkScan.h"
26#include "SkShader.h"
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000027#include "SkSmallAllocator.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +000028#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkStroke.h"
halcanary435657f2015-09-15 12:53:07 -070030#include "SkStrokeRec.h"
herbf553e4e2015-11-09 08:51:56 -080031#include "SkTemplates.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070032#include "SkTextMapStateProc.h"
reed@google.com32e5d972011-07-27 18:25:57 +000033#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000034#include "SkUtils.h"
commit-bot@chromium.org559a8832014-05-30 10:08:22 +000035#include "SkVertState.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,
reed1ec04d92016-08-05 12:07:41 -070086 SkShader::kClamp_TileMode, localMatrix,
87 kNever_SkCopyPixelsMode,
88 &fAllocator));
reed@google.com40c2ba22011-07-25 19:41:22 +000089 // we deliberately left the shader with an owner-count of 2
reed8a21c9f2016-03-08 18:50:00 -080090 fPaint.getShader()->ref();
reed@google.com40c2ba22011-07-25 19:41:22 +000091 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 }
reed@google.com82065d62011-02-07 15:30:46 +000093
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 ~SkAutoBitmapShaderInstall() {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +000095 // since fAllocator will destroy shader, we insist that owners == 2
96 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000097
halcanary96fcdcc2015-08-27 07:41:13 -070098 fPaint.setShader(nullptr); // unref the shader by 1
reed@android.com8a1c16f2008-12-17 15:59:43 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 }
reed@google.com82065d62011-02-07 15:30:46 +0000101
reed@google.com40c2ba22011-07-25 19:41:22 +0000102 // return the new paint that has the shader applied
103 const SkPaint& paintWithShader() const { return fPaint; }
104
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105private:
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000106 // copy of caller's paint (which we then modify)
107 SkPaint fPaint;
108 // Stores the shader.
109 SkTBlitterAllocator fAllocator;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000111#define SkAutoBitmapShaderInstall(...) SK_REQUIRE_LOCAL_VAR(SkAutoBitmapShaderInstall)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113///////////////////////////////////////////////////////////////////////////////
114
reed@android.comf2b98d62010-12-20 18:26:13 +0000115SkDraw::SkDraw() {
116 sk_bzero(this, sizeof(*this));
117}
118
reed@google.com4bbdeac2013-01-24 21:03:11 +0000119bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
120 if (fRC->isEmpty()) {
121 return false;
122 }
123
124 SkMatrix inverse;
125 if (!fMatrix->invert(&inverse)) {
126 return false;
127 }
128
129 SkIRect devBounds = fRC->getBounds();
130 // outset to have slop for antialasing and hairlines
131 devBounds.outset(1, 1);
132 inverse.mapRect(localBounds, SkRect::Make(devBounds));
133 return true;
134}
135
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136///////////////////////////////////////////////////////////////////////////////
137
138typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
139
140static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +0000141 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142}
143
144static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
145
146static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000147 sk_memset32((uint32_t*)pixels, data, SkToInt(bytes >> 2));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148}
149
150static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000151 sk_memset16((uint16_t*)pixels, data, SkToInt(bytes >> 1));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152}
153
154static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
155 memset(pixels, data, bytes);
156}
157
reed41e010c2015-06-09 12:16:53 -0700158static BitmapXferProc ChooseBitmapXferProc(const SkPixmap& dst, const SkPaint& paint,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 uint32_t* data) {
160 // todo: we can apply colorfilter up front if no shader, so we wouldn't
161 // need to abort this fastpath
162 if (paint.getShader() || paint.getColorFilter()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700163 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 }
165
reed374772b2016-10-05 17:33:02 -0700166 SkBlendMode mode = paint.getBlendMode();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000168
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 // collaps modes based on color...
reed374772b2016-10-05 17:33:02 -0700170 if (SkBlendMode::kSrcOver == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 unsigned alpha = SkColorGetA(color);
172 if (0 == alpha) {
reed374772b2016-10-05 17:33:02 -0700173 mode = SkBlendMode::kDst;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 } else if (0xFF == alpha) {
reed374772b2016-10-05 17:33:02 -0700175 mode = SkBlendMode::kSrc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 }
177 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 switch (mode) {
reed374772b2016-10-05 17:33:02 -0700180 case SkBlendMode::kClear:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181// SkDebugf("--- D_Clear_BitmapXferProc\n");
182 return D_Clear_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700183 case SkBlendMode::kDst:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184// SkDebugf("--- D_Dst_BitmapXferProc\n");
185 return D_Dst_BitmapXferProc; // ignore data
reed374772b2016-10-05 17:33:02 -0700186 case SkBlendMode::kSrc: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000188 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 */
190 SkPMColor pmc = SkPreMultiplyColor(color);
reed41e010c2015-06-09 12:16:53 -0700191 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000192 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 if (data) {
194 *data = pmc;
195 }
196// SkDebugf("--- D32_Src_BitmapXferProc\n");
197 return D32_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000198 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 if (data) {
200 *data = SkPixel32ToPixel16(pmc);
201 }
202// SkDebugf("--- D16_Src_BitmapXferProc\n");
203 return D16_Src_BitmapXferProc;
reed@google.com900ecf22014-02-20 20:55:37 +0000204 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 if (data) {
206 *data = SkGetPackedA32(pmc);
207 }
208// SkDebugf("--- DA8_Src_BitmapXferProc\n");
209 return DA8_Src_BitmapXferProc;
210 default:
211 break;
212 }
213 break;
214 }
215 default:
216 break;
217 }
halcanary96fcdcc2015-08-27 07:41:13 -0700218 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219}
220
reed41e010c2015-06-09 12:16:53 -0700221static void CallBitmapXferProc(const SkPixmap& dst, const SkIRect& rect, BitmapXferProc proc,
222 uint32_t procData) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 int shiftPerPixel;
reed41e010c2015-06-09 12:16:53 -0700224 switch (dst.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000225 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 shiftPerPixel = 2;
227 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000228 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 shiftPerPixel = 1;
230 break;
reed@google.com900ecf22014-02-20 20:55:37 +0000231 case kAlpha_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 shiftPerPixel = 0;
233 break;
234 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000235 SkDEBUGFAIL("Can't use xferproc on this config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 return;
237 }
238
reed41e010c2015-06-09 12:16:53 -0700239 uint8_t* pixels = (uint8_t*)dst.writable_addr();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 SkASSERT(pixels);
reed41e010c2015-06-09 12:16:53 -0700241 const size_t rowBytes = dst.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 const int widthBytes = rect.width() << shiftPerPixel;
243
244 // skip down to the first scanline and X position
245 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
246 for (int scans = rect.height() - 1; scans >= 0; --scans) {
247 proc(pixels, widthBytes, procData);
248 pixels += rowBytes;
249 }
250}
251
252void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000253 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254
reed@google.com045e62d2011-10-24 12:19:46 +0000255 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 return;
257 }
258
259 SkIRect devRect;
reed41e010c2015-06-09 12:16:53 -0700260 devRect.set(0, 0, fDst.width(), fDst.height());
reed@google.coma76de3d2011-01-13 18:30:42 +0000261
reed@google.com045e62d2011-10-24 12:19:46 +0000262 if (fRC->isBW()) {
263 /* If we don't have a shader (i.e. we're just a solid color) we may
264 be faster to operate directly on the device bitmap, rather than invoking
265 a blitter. Esp. true for xfermodes, which require a colorshader to be
266 present, which is just redundant work. Since we're drawing everywhere
267 in the clip, we don't have to worry about antialiasing.
268 */
269 uint32_t procData = 0; // to avoid the warning
reed41e010c2015-06-09 12:16:53 -0700270 BitmapXferProc proc = ChooseBitmapXferProc(fDst, paint, &procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000271 if (proc) {
272 if (D_Dst_BitmapXferProc == proc) { // nothing to do
273 return;
274 }
275
276 SkRegion::Iterator iter(fRC->bwRgn());
277 while (!iter.done()) {
reed41e010c2015-06-09 12:16:53 -0700278 CallBitmapXferProc(fDst, iter.rect(), proc, procData);
reed@google.com045e62d2011-10-24 12:19:46 +0000279 iter.next();
280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 return;
282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 }
reed@google.com045e62d2011-10-24 12:19:46 +0000284
285 // normal case: use a blitter
reed41e010c2015-06-09 12:16:53 -0700286 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000287 SkScan::FillIRect(devRect, *fRC, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288}
289
290///////////////////////////////////////////////////////////////////////////////
291
292struct PtProcRec {
293 SkCanvas::PointMode fMode;
294 const SkPaint* fPaint;
295 const SkRegion* fClip;
reed@google.com045e62d2011-10-24 12:19:46 +0000296 const SkRasterClip* fRC;
reed@google.coma76de3d2011-01-13 18:30:42 +0000297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 // computed values
299 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
302 SkBlitter*);
303
304 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000305 const SkRasterClip*);
306 Proc chooseProc(SkBlitter** blitter);
307
308private:
309 SkAAClipBlitterWrapper fWrapper;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310};
311
312static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
313 int count, SkBlitter* blitter) {
314 SkASSERT(rec.fClip->isRect());
315 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000318 int x = SkScalarFloorToInt(devPts[i].fX);
319 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 if (r.contains(x, y)) {
321 blitter->blitH(x, y, 1);
322 }
323 }
324}
325
326static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
327 const SkPoint devPts[], int count,
328 SkBlitter* blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +0000329 SkASSERT(rec.fRC->isRect());
330 const SkIRect& r = rec.fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700332 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
333 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000334
reed41e010c2015-06-09 12:16:53 -0700335 uint16_t* addr = dst->writable_addr16(0, 0);
336 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000337
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 for (int i = 0; i < count; i++) {
reed@google.com2d47a212012-11-29 21:01:00 +0000339 int x = SkScalarFloorToInt(devPts[i].fX);
340 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 if (r.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
343 }
344 }
345}
346
reed@google.com2d47a212012-11-29 21:01:00 +0000347static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
348 const SkPoint devPts[], int count,
349 SkBlitter* blitter) {
350 SkASSERT(rec.fRC->isRect());
351 const SkIRect& r = rec.fRC->getBounds();
352 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700353 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
354 SkASSERT(dst);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000355
reed41e010c2015-06-09 12:16:53 -0700356 SkPMColor* addr = dst->writable_addr32(0, 0);
357 size_t rb = dst->rowBytes();
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000358
reed@google.com2d47a212012-11-29 21:01:00 +0000359 for (int i = 0; i < count; i++) {
360 int x = SkScalarFloorToInt(devPts[i].fX);
361 int y = SkScalarFloorToInt(devPts[i].fY);
362 if (r.contains(x, y)) {
363 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
364 }
365 }
366}
367
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
369 int count, SkBlitter* blitter) {
370 for (int i = 0; i < count; i++) {
reed@google.come1ca7052013-12-17 19:22:07 +0000371 int x = SkScalarFloorToInt(devPts[i].fX);
372 int y = SkScalarFloorToInt(devPts[i].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 if (rec.fClip->contains(x, y)) {
374 blitter->blitH(x, y, 1);
375 }
376 }
377}
378
379static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
380 int count, SkBlitter* blitter) {
381 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700382 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 }
384}
385
386static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
387 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700388 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389}
390
391// aa versions
392
393static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
394 int count, SkBlitter* blitter) {
395 for (int i = 0; i < count; i += 2) {
reed5dc6b7d2015-04-14 10:40:44 -0700396 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 }
398}
399
400static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
401 int count, SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700402 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403}
404
405// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
406
407static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
408 int count, SkBlitter* blitter) {
409 const SkFixed radius = rec.fRadius;
410 for (int i = 0; i < count; i++) {
411 SkFixed x = SkScalarToFixed(devPts[i].fX);
412 SkFixed y = SkScalarToFixed(devPts[i].fY);
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000413
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414 SkXRect r;
415 r.fLeft = x - radius;
416 r.fTop = y - radius;
417 r.fRight = x + radius;
418 r.fBottom = y + radius;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000419
reed@google.com045e62d2011-10-24 12:19:46 +0000420 SkScan::FillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 }
422}
423
424static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
425 int count, SkBlitter* blitter) {
426 const SkFixed radius = rec.fRadius;
427 for (int i = 0; i < count; i++) {
428 SkFixed x = SkScalarToFixed(devPts[i].fX);
429 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000430
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 SkXRect r;
432 r.fLeft = x - radius;
433 r.fTop = y - radius;
434 r.fRight = x + radius;
435 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000436
reed@google.com045e62d2011-10-24 12:19:46 +0000437 SkScan::AntiFillXRect(r, *rec.fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 }
439}
440
reed@android.comb4f404a2009-07-10 17:02:17 +0000441// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
reed@google.com045e62d2011-10-24 12:19:46 +0000443 const SkMatrix* matrix, const SkRasterClip* rc) {
ochang3ece53e2015-05-21 15:44:53 -0700444 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
445 return false;
446 }
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 if (paint.getPathEffect()) {
449 return false;
450 }
451 SkScalar width = paint.getStrokeWidth();
452 if (0 == width) {
453 fMode = mode;
454 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700455 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000456 fRC = rc;
reed@google.com2d47a212012-11-29 21:01:00 +0000457 fRadius = SK_FixedHalf;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 return true;
459 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000460 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
robertphillips9f2251c2014-11-04 13:33:50 -0800461 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
463 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
464 if (SkScalarNearlyZero(sx - sy)) {
465 if (sx < 0) {
466 sx = -sx;
467 }
468
469 fMode = mode;
470 fPaint = &paint;
halcanary96fcdcc2015-08-27 07:41:13 -0700471 fClip = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +0000472 fRC = rc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
474 return true;
475 }
476 }
477 return false;
478}
479
reed@google.com045e62d2011-10-24 12:19:46 +0000480PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700481 Proc proc = nullptr;
reed@google.coma76de3d2011-01-13 18:30:42 +0000482
reed@google.com045e62d2011-10-24 12:19:46 +0000483 SkBlitter* blitter = *blitterPtr;
484 if (fRC->isBW()) {
485 fClip = &fRC->bwRgn();
486 } else {
487 fWrapper.init(*fRC, blitter);
488 fClip = &fWrapper.getRgn();
489 blitter = fWrapper.getBlitter();
490 *blitterPtr = blitter;
491 }
492
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 // for our arrays
494 SkASSERT(0 == SkCanvas::kPoints_PointMode);
495 SkASSERT(1 == SkCanvas::kLines_PointMode);
496 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
497 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
498
reed@google.com2d47a212012-11-29 21:01:00 +0000499 if (fPaint->isAntiAlias()) {
500 if (0 == fPaint->getStrokeWidth()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 static const Proc gAAProcs[] = {
502 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
503 };
504 proc = gAAProcs[fMode];
reed@google.com2d47a212012-11-29 21:01:00 +0000505 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
506 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
507 proc = aa_square_proc;
508 }
509 } else { // BW
510 if (fRadius <= SK_FixedHalf) { // small radii and hairline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
512 uint32_t value;
reed41e010c2015-06-09 12:16:53 -0700513 const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
reed@google.com900ecf22014-02-20 20:55:37 +0000514 if (bm && kRGB_565_SkColorType == bm->colorType()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 proc = bw_pt_rect_16_hair_proc;
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000516 } else if (bm && kN32_SkColorType == bm->colorType()) {
reed@google.com2d47a212012-11-29 21:01:00 +0000517 proc = bw_pt_rect_32_hair_proc;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 } else {
519 proc = bw_pt_rect_hair_proc;
520 }
521 } else {
522 static Proc gBWProcs[] = {
523 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
524 };
525 proc = gBWProcs[fMode];
526 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 } else {
528 proc = bw_square_proc;
529 }
530 }
531 return proc;
532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534// each of these costs 8-bytes of stack space, so don't make it too large
535// must be even for lines/polygon to work
536#define MAX_DEV_PTS 32
537
538void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000539 const SkPoint pts[], const SkPaint& paint,
540 bool forceUseDevice) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 // if we're in lines mode, force count to be even
542 if (SkCanvas::kLines_PointMode == mode) {
543 count &= ~(size_t)1;
544 }
545
546 if ((long)count <= 0) {
547 return;
548 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000549
halcanary96fcdcc2015-08-27 07:41:13 -0700550 SkASSERT(pts != nullptr);
reed@android.comf2b98d62010-12-20 18:26:13 +0000551 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000554 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 return;
556 }
557
558 PtProcRec rec;
reed@google.com045e62d2011-10-24 12:19:46 +0000559 if (!forceUseDevice && rec.init(mode, paint, fMatrix, fRC)) {
reed41e010c2015-06-09 12:16:53 -0700560 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561
562 SkPoint devPts[MAX_DEV_PTS];
563 const SkMatrix* matrix = fMatrix;
564 SkBlitter* bltr = blitter.get();
reed@google.com045e62d2011-10-24 12:19:46 +0000565 PtProcRec::Proc proc = rec.chooseProc(&bltr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 // we have to back up subsequent passes if we're in polygon mode
567 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000568
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569 do {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000570 int n = SkToInt(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 if (n > MAX_DEV_PTS) {
572 n = MAX_DEV_PTS;
573 }
574 matrix->mapPoints(devPts, pts, n);
575 proc(rec, devPts, n, bltr);
576 pts += n - backup;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000577 SkASSERT(SkToInt(count) >= n);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 count -= n;
579 if (count > 0) {
580 count += backup;
581 }
582 } while (count != 0);
583 } else {
584 switch (mode) {
585 case SkCanvas::kPoints_PointMode: {
586 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000587 SkPaint newPaint(paint);
588 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589
reed@google.com40c2ba22011-07-25 19:41:22 +0000590 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000592
reed@google.com40c2ba22011-07-25 19:41:22 +0000593 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkPath path;
595 SkMatrix preMatrix;
mtklein7ef849d2014-11-24 09:11:45 -0800596
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 path.addCircle(0, 0, radius);
598 for (size_t i = 0; i < count; i++) {
599 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
600 // pass true for the last point, since we can modify
601 // then path then
jvanverthb3eb6872014-10-24 07:12:51 -0700602 path.setIsVolatile((count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000603 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000604 fDevice->drawPath(*this, path, newPaint, &preMatrix,
reed@android.comf2b98d62010-12-20 18:26:13 +0000605 (count-1) == i);
606 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000607 this->drawPath(path, newPaint, &preMatrix,
608 (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000609 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 }
611 } else {
612 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 for (size_t i = 0; i < count; i++) {
615 r.fLeft = pts[i].fX - radius;
616 r.fTop = pts[i].fY - radius;
617 r.fRight = r.fLeft + width;
618 r.fBottom = r.fTop + width;
reed@android.comf2b98d62010-12-20 18:26:13 +0000619 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000620 fDevice->drawRect(*this, r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000621 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000622 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000623 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 }
625 }
626 break;
627 }
628 case SkCanvas::kLines_PointMode:
bsalomon49f085d2014-09-05 13:34:00 -0700629 if (2 == count && paint.getPathEffect()) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000630 // most likely a dashed line - see if it is one of the ones
631 // we can accelerate
632 SkStrokeRec rec(paint);
robertphillips@google.com6d875572012-12-17 18:56:29 +0000633 SkPathEffect::PointData pointData;
robertphillips@google.com629ab542012-11-28 17:18:11 +0000634
635 SkPath path;
636 path.moveTo(pts[0]);
637 path.lineTo(pts[1]);
638
reed@google.com4bbdeac2013-01-24 21:03:11 +0000639 SkRect cullRect = SkRect::Make(fRC->getBounds());
skia.committer@gmail.com4024f322013-01-25 07:06:46 +0000640
reed@google.com4bbdeac2013-01-24 21:03:11 +0000641 if (paint.getPathEffect()->asPoints(&pointData, path, rec,
642 *fMatrix, &cullRect)) {
robertphillips@google.com6d875572012-12-17 18:56:29 +0000643 // 'asPoints' managed to find some fast path
644
robertphillips@google.com629ab542012-11-28 17:18:11 +0000645 SkPaint newP(paint);
halcanary96fcdcc2015-08-27 07:41:13 -0700646 newP.setPathEffect(nullptr);
robertphillips@google.com935ad022012-12-05 19:07:21 +0000647 newP.setStyle(SkPaint::kFill_Style);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000648
robertphillips@google.com6d875572012-12-17 18:56:29 +0000649 if (!pointData.fFirst.isEmpty()) {
650 if (fDevice) {
651 fDevice->drawPath(*this, pointData.fFirst, newP);
652 } else {
653 this->drawPath(pointData.fFirst, newP);
654 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000655 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000656
657 if (!pointData.fLast.isEmpty()) {
658 if (fDevice) {
659 fDevice->drawPath(*this, pointData.fLast, newP);
660 } else {
661 this->drawPath(pointData.fLast, newP);
662 }
robertphillips@google.com935ad022012-12-05 19:07:21 +0000663 }
robertphillips@google.com6d875572012-12-17 18:56:29 +0000664
665 if (pointData.fSize.fX == pointData.fSize.fY) {
666 // The rest of the dashed line can just be drawn as points
667 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
668
669 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
670 newP.setStrokeCap(SkPaint::kRound_Cap);
671 } else {
672 newP.setStrokeCap(SkPaint::kButt_Cap);
673 }
674
675 if (fDevice) {
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000676 fDevice->drawPoints(*this,
robertphillips@google.com6d875572012-12-17 18:56:29 +0000677 SkCanvas::kPoints_PointMode,
678 pointData.fNumPoints,
679 pointData.fPoints,
680 newP);
681 } else {
682 this->drawPoints(SkCanvas::kPoints_PointMode,
683 pointData.fNumPoints,
684 pointData.fPoints,
685 newP,
686 forceUseDevice);
687 }
688 break;
689 } else {
690 // The rest of the dashed line must be drawn as rects
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000691 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
robertphillips@google.com6d875572012-12-17 18:56:29 +0000692 pointData.fFlags));
693
694 SkRect r;
695
696 for (int i = 0; i < pointData.fNumPoints; ++i) {
697 r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
698 pointData.fPoints[i].fY - pointData.fSize.fY,
699 pointData.fPoints[i].fX + pointData.fSize.fX,
700 pointData.fPoints[i].fY + pointData.fSize.fY);
701 if (fDevice) {
702 fDevice->drawRect(*this, r, newP);
703 } else {
704 this->drawRect(r, newP);
705 }
706 }
707 }
708
robertphillips@google.com629ab542012-11-28 17:18:11 +0000709 break;
710 }
711 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000712 // couldn't take fast path so fall through!
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 case SkCanvas::kPolygon_PointMode: {
714 count -= 1;
715 SkPath path;
716 SkPaint p(paint);
717 p.setStyle(SkPaint::kStroke_Style);
718 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
jvanverthb3eb6872014-10-24 07:12:51 -0700719 path.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 for (size_t i = 0; i < count; i += inc) {
721 path.moveTo(pts[i]);
722 path.lineTo(pts[i+1]);
reed@android.comf2b98d62010-12-20 18:26:13 +0000723 if (fDevice) {
halcanary96fcdcc2015-08-27 07:41:13 -0700724 fDevice->drawPath(*this, path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000725 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700726 this->drawPath(path, p, nullptr, true);
reed@android.comf2b98d62010-12-20 18:26:13 +0000727 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 path.rewind();
729 }
730 break;
731 }
732 }
733 }
734}
735
fmalita1a178ca2015-01-15 06:01:23 -0800736static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
737 SkASSERT(matrix.rectStaysRect());
738 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
739
740 SkVector size;
741 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
742 matrix.mapVectors(&size, &pt, 1);
743 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
744}
745
reed@google.com761fb622011-04-04 18:58:05 +0000746static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
747 SkPoint* strokeSize) {
748 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
749 paint.getStrokeMiter() < SK_ScalarSqrt2) {
750 return false;
751 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000752
fmalita1a178ca2015-01-15 06:01:23 -0800753 *strokeSize = compute_stroke_size(paint, matrix);
reed@google.com761fb622011-04-04 18:58:05 +0000754 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000755}
756
reed@google.com62ab7ad2011-04-05 14:08:25 +0000757SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
758 const SkMatrix& matrix,
759 SkPoint* strokeSize) {
760 RectType rtype;
761 const SkScalar width = paint.getStrokeWidth();
762 const bool zeroWidth = (0 == width);
763 SkPaint::Style style = paint.getStyle();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000764
reed@google.com62ab7ad2011-04-05 14:08:25 +0000765 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
766 style = SkPaint::kFill_Style;
767 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000768
reed@google.com62ab7ad2011-04-05 14:08:25 +0000769 if (paint.getPathEffect() || paint.getMaskFilter() ||
770 paint.getRasterizer() || !matrix.rectStaysRect() ||
771 SkPaint::kStrokeAndFill_Style == style) {
772 rtype = kPath_RectType;
773 } else if (SkPaint::kFill_Style == style) {
774 rtype = kFill_RectType;
775 } else if (zeroWidth) {
776 rtype = kHair_RectType;
777 } else if (easy_rect_join(paint, matrix, strokeSize)) {
778 rtype = kStroke_RectType;
779 } else {
780 rtype = kPath_RectType;
781 }
782 return rtype;
783}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000784
reed@google.com73244152012-05-11 14:35:23 +0000785static const SkPoint* rect_points(const SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000786 return SkTCast<const SkPoint*>(&r);
reed@google.com73244152012-05-11 14:35:23 +0000787}
788
789static SkPoint* rect_points(SkRect& r) {
reed@google.comfc2f0d02013-03-29 14:23:56 +0000790 return SkTCast<SkPoint*>(&r);
reed@google.com40c2ba22011-07-25 19:41:22 +0000791}
792
reed03939122014-12-15 13:42:51 -0800793void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
794 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000795 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796
797 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +0000798 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 return;
800 }
801
reed03939122014-12-15 13:42:51 -0800802 const SkMatrix* matrix;
803 SkMatrix combinedMatrixStorage;
804 if (paintMatrix) {
805 SkASSERT(postPaintRect);
806 combinedMatrixStorage.setConcat(*fMatrix, *paintMatrix);
807 matrix = &combinedMatrixStorage;
808 } else {
809 SkASSERT(!postPaintRect);
810 matrix = fMatrix;
811 }
812
reed@google.com761fb622011-04-04 18:58:05 +0000813 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000814 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000815
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000816 if (kPath_RectType == rtype) {
reed03939122014-12-15 13:42:51 -0800817 SkDraw draw(*this);
818 if (paintMatrix) {
819 draw.fMatrix = matrix;
820 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 SkPath tmp;
reed03939122014-12-15 13:42:51 -0800822 tmp.addRect(prePaintRect);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 tmp.setFillType(SkPath::kWinding_FillType);
halcanary96fcdcc2015-08-27 07:41:13 -0700824 draw.drawPath(tmp, paint, nullptr, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 return;
826 }
827
reed03939122014-12-15 13:42:51 -0800828 SkRect devRect;
fmalita1a178ca2015-01-15 06:01:23 -0800829 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
830 // skip the paintMatrix when transforming the rect by the CTM
831 fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
reed@google.com73244152012-05-11 14:35:23 +0000832 devRect.sort();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 // look for the quick exit, before we build a blitter
fmalita1a178ca2015-01-15 06:01:23 -0800835 SkRect bbox = devRect;
reed@google.com1c028bd2013-08-28 15:23:19 +0000836 if (paint.getStyle() != SkPaint::kFill_Style) {
837 // extra space for hairlines
georgeb3eba472014-09-09 11:33:57 -0700838 if (paint.getStrokeWidth() == 0) {
fmalita1a178ca2015-01-15 06:01:23 -0800839 bbox.outset(1, 1);
georgeb3eba472014-09-09 11:33:57 -0700840 } else {
fmalita1a178ca2015-01-15 06:01:23 -0800841 // For kStroke_RectType, strokeSize is already computed.
842 const SkPoint& ssize = (kStroke_RectType == rtype)
843 ? strokeSize
844 : compute_stroke_size(paint, *fMatrix);
845 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
georgeb3eba472014-09-09 11:33:57 -0700846 }
reed@google.com1c028bd2013-08-28 15:23:19 +0000847 }
fmalita1a178ca2015-01-15 06:01:23 -0800848
849 SkIRect ir = bbox.roundOut();
reed@google.com1c028bd2013-08-28 15:23:19 +0000850 if (fRC->quickReject(ir)) {
851 return;
rmistry@google.come09d6f42013-08-27 18:53:41 +0000852 }
853
reed41e010c2015-06-09 12:16:53 -0700854 SkDeviceLooper looper(fDst, *fRC, ir, paint.isAntiAlias());
reed@google.com1c028bd2013-08-28 15:23:19 +0000855 while (looper.next()) {
856 SkRect localDevRect;
857 looper.mapRect(&localDevRect, devRect);
858 SkMatrix localMatrix;
reed03939122014-12-15 13:42:51 -0800859 looper.mapMatrix(&localMatrix, *matrix);
rmistry@google.come09d6f42013-08-27 18:53:41 +0000860
reed41e010c2015-06-09 12:16:53 -0700861 SkAutoBlitterChoose blitterStorage(looper.getPixmap(), localMatrix, paint);
reed@google.com1c028bd2013-08-28 15:23:19 +0000862 const SkRasterClip& clip = looper.getRC();
863 SkBlitter* blitter = blitterStorage.get();
864
865 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
866 // case we are also hairline (if we've gotten to here), which devolves to
867 // effectively just kFill
868 switch (rtype) {
869 case kFill_RectType:
870 if (paint.isAntiAlias()) {
871 SkScan::AntiFillRect(localDevRect, clip, blitter);
872 } else {
873 SkScan::FillRect(localDevRect, clip, blitter);
874 }
875 break;
876 case kStroke_RectType:
877 if (paint.isAntiAlias()) {
878 SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
879 } else {
880 SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
881 }
882 break;
883 case kHair_RectType:
884 if (paint.isAntiAlias()) {
885 SkScan::AntiHairRect(localDevRect, clip, blitter);
886 } else {
887 SkScan::HairRect(localDevRect, clip, blitter);
888 }
889 break;
890 default:
891 SkDEBUGFAIL("bad rtype");
892 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 }
894}
895
896void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
897 if (srcM.fBounds.isEmpty()) {
898 return;
899 }
900
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000901 const SkMask* mask = &srcM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902
bungeman@google.com0a60b3d2011-09-26 19:09:08 +0000903 SkMask dstM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 if (paint.getMaskFilter() &&
robertphillipse80eb922015-12-17 11:33:12 -0800905 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 mask = &dstM;
907 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000908 SkAutoMaskFreeImage ami(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909
reed41e010c2015-06-09 12:16:53 -0700910 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
reed@google.com045e62d2011-10-24 12:19:46 +0000911 SkBlitter* blitter = blitterChooser.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912
reed@google.com045e62d2011-10-24 12:19:46 +0000913 SkAAClipBlitterWrapper wrapper;
914 const SkRegion* clipRgn;
915
916 if (fRC->isBW()) {
917 clipRgn = &fRC->bwRgn();
918 } else {
919 wrapper.init(*fRC, blitter);
920 clipRgn = &wrapper.getRgn();
921 blitter = wrapper.getBlitter();
922 }
923 blitter->blitMaskRegion(*mask, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924}
925
reed@android.comebdeeb82009-09-03 21:45:49 +0000926static SkScalar fast_len(const SkVector& vec) {
927 SkScalar x = SkScalarAbs(vec.fX);
928 SkScalar y = SkScalarAbs(vec.fY);
929 if (x < y) {
930 SkTSwap(x, y);
931 }
932 return x + SkScalarHalf(y);
933}
934
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000935bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
936 SkScalar* coverage) {
937 SkASSERT(strokeWidth > 0);
938 // We need to try to fake a thick-stroke with a modulated hairline.
reed@google.comecadf992011-09-19 19:18:18 +0000939
tomhudson@google.com8d430182011-06-06 19:11:19 +0000940 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000941 return false;
942 }
reed@google.comecadf992011-09-19 19:18:18 +0000943
reed@android.comebdeeb82009-09-03 21:45:49 +0000944 SkVector src[2], dst[2];
reed@google.comecadf992011-09-19 19:18:18 +0000945 src[0].set(strokeWidth, 0);
946 src[1].set(0, strokeWidth);
reed@android.comebdeeb82009-09-03 21:45:49 +0000947 matrix.mapVectors(dst, src, 2);
948 SkScalar len0 = fast_len(dst[0]);
949 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000950 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
bsalomon49f085d2014-09-05 13:34:00 -0700951 if (coverage) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000952 *coverage = SkScalarAve(len0, len1);
953 }
reed@android.comebdeeb82009-09-03 21:45:49 +0000954 return true;
955 }
956 return false;
957}
958
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000959void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
960 SkDEBUGCODE(this->validate());
961
962 if (fRC->isEmpty()) {
963 return;
964 }
965
966 {
967 // TODO: Investigate optimizing these options. They are in the same
968 // order as SkDraw::drawPath, which handles each case. It may be
969 // that there is no way to optimize for these using the SkRRect path.
970 SkScalar coverage;
971 if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
972 goto DRAW_PATH;
973 }
974
975 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
976 goto DRAW_PATH;
977 }
978
979 if (paint.getRasterizer()) {
980 goto DRAW_PATH;
981 }
982 }
983
984 if (paint.getMaskFilter()) {
985 // Transform the rrect into device space.
986 SkRRect devRRect;
987 if (rrect.transform(*fMatrix, &devRRect)) {
reed41e010c2015-06-09 12:16:53 -0700988 SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
bsalomon055e1922016-05-06 07:22:58 -0700989 if (paint.getMaskFilter()->filterRRect(devRRect, *fMatrix, *fRC, blitter.get())) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000990 return; // filterRRect() called the blitter, so we're done
991 }
992 }
993 }
994
995DRAW_PATH:
996 // Now fall back to the default case of using a path.
997 SkPath path;
998 path.addRRect(rrect);
halcanary96fcdcc2015-08-27 07:41:13 -0700999 this->drawPath(path, paint, nullptr, true);
scroggo@google.coma8e33a92013-11-08 18:02:53 +00001000}
1001
caryclark1a7eb262016-01-21 07:07:02 -08001002SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
reed05d90442015-02-12 13:35:52 -08001003 if (!matrix.hasPerspective()) {
1004 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
1005 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
1006 if (SkScalarsAreFinite(sx, sy)) {
lsalzmana4b87042016-07-06 12:19:56 -07001007 SkScalar scale = SkTMax(sx, sy);
1008 if (scale > 0) {
1009 return scale;
1010 }
reed05d90442015-02-12 13:35:52 -08001011 }
1012 }
1013 return 1;
1014}
1015
reed82595b62016-05-09 17:48:46 -07001016void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
1017 SkBlitter* customBlitter, bool doFill) const {
reed01a2ff82016-07-18 13:22:55 -07001018 // Do a conservative quick-reject test, since a looper or other modifier may have moved us
1019 // out of range.
1020 if (!devPath.isInverseFillType()) {
1021 // If we're a H or V line, our bounds will be empty. So we bloat here just so we don't
1022 // appear empty to the intersects call. This also gives us slop in case we're antialiasing
1023 SkRect pathBounds = devPath.getBounds().makeOutset(1, 1);
1024
1025 if (paint.getMaskFilter()) {
1026 paint.getMaskFilter()->computeFastBounds(pathBounds, &pathBounds);
1027
1028 // Need to outset the path to work-around a bug in blurmaskfilter. When that is fixed
1029 // we can remove this hack. See skbug.com/5542
1030 pathBounds.outset(7, 7);
1031 }
1032
1033 // Now compare against the clip's bounds
1034 if (!SkRect::Make(fRC->getBounds()).intersects(pathBounds)) {
1035 return;
1036 }
1037 }
1038
reed82595b62016-05-09 17:48:46 -07001039 SkBlitter* blitter = nullptr;
1040 SkAutoBlitterChoose blitterStorage;
1041 if (nullptr == customBlitter) {
1042 blitterStorage.choose(fDst, *fMatrix, paint, drawCoverage);
1043 blitter = blitterStorage.get();
1044 } else {
1045 blitter = customBlitter;
1046 }
1047
1048 if (paint.getMaskFilter()) {
1049 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
1050 : SkStrokeRec::kHairline_InitStyle;
1051 if (paint.getMaskFilter()->filterPath(devPath, *fMatrix, *fRC, blitter, style)) {
1052 return; // filterPath() called the blitter, so we're done
1053 }
1054 }
1055
1056 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
1057 if (doFill) {
1058 if (paint.isAntiAlias()) {
1059 proc = SkScan::AntiFillPath;
1060 } else {
1061 proc = SkScan::FillPath;
1062 }
1063 } else { // hairline
1064 if (paint.isAntiAlias()) {
1065 switch (paint.getStrokeCap()) {
1066 case SkPaint::kButt_Cap:
1067 proc = SkScan::AntiHairPath;
1068 break;
1069 case SkPaint::kSquare_Cap:
1070 proc = SkScan::AntiHairSquarePath;
1071 break;
1072 case SkPaint::kRound_Cap:
1073 proc = SkScan::AntiHairRoundPath;
1074 break;
1075 default:
1076 proc SK_INIT_TO_AVOID_WARNING;
1077 SkDEBUGFAIL("unknown paint cap type");
1078 }
1079 } else {
1080 switch (paint.getStrokeCap()) {
1081 case SkPaint::kButt_Cap:
1082 proc = SkScan::HairPath;
1083 break;
1084 case SkPaint::kSquare_Cap:
1085 proc = SkScan::HairSquarePath;
1086 break;
1087 case SkPaint::kRound_Cap:
1088 proc = SkScan::HairRoundPath;
1089 break;
1090 default:
1091 proc SK_INIT_TO_AVOID_WARNING;
1092 SkDEBUGFAIL("unknown paint cap type");
1093 }
1094 }
1095 }
1096 proc(devPath, *fRC, blitter);
1097}
1098
reed@google.com32e5d972011-07-27 18:25:57 +00001099void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
reed@google.com126f7f52013-11-07 16:06:53 +00001100 const SkMatrix* prePathMatrix, bool pathIsMutable,
krajcevski53f09592014-08-06 11:12:14 -07001101 bool drawCoverage, SkBlitter* customBlitter) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001102 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103
1104 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001105 if (fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 return;
1107 }
1108
1109 SkPath* pathPtr = (SkPath*)&origSrcPath;
1110 bool doFill = true;
1111 SkPath tmpPath;
1112 SkMatrix tmpMatrix;
1113 const SkMatrix* matrix = fMatrix;
jvanverthb3eb6872014-10-24 07:12:51 -07001114 tmpPath.setIsVolatile(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115
1116 if (prePathMatrix) {
reed@google.com32e5d972011-07-27 18:25:57 +00001117 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
1118 origPaint.getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +00001120
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 if (!pathIsMutable) {
1122 result = &tmpPath;
1123 pathIsMutable = true;
1124 }
1125 pathPtr->transform(*prePathMatrix, result);
1126 pathPtr = result;
1127 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001128 tmpMatrix.setConcat(*matrix, *prePathMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 matrix = &tmpMatrix;
1130 }
1131 }
1132 // at this point we're done with prePathMatrix
1133 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +00001134
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001135 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
reed@google.coma76de3d2011-01-13 18:30:42 +00001136
reed@google.comecadf992011-09-19 19:18:18 +00001137 {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001138 SkScalar coverage;
1139 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
1140 if (SK_Scalar1 == coverage) {
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001141 paint.writable()->setStrokeWidth(0);
reed374772b2016-10-05 17:33:02 -07001142 } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001143 U8CPU newAlpha;
1144#if 0
1145 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
1146 origPaint.getAlpha()));
1147#else
1148 // this is the old technique, which we preserve for now so
1149 // we don't change previous results (testing)
1150 // the new way seems fine, its just (a tiny bit) different
1151 int scale = (int)SkScalarMul(coverage, 256);
1152 newAlpha = origPaint.getAlpha() * scale >> 8;
1153#endif
bsalomon@google.com5dc26b92012-10-11 19:32:32 +00001154 SkPaint* writablePaint = paint.writable();
1155 writablePaint->setStrokeWidth(0);
1156 writablePaint->setAlpha(newAlpha);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001157 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 }
1159 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001160
reed@google.com32e5d972011-07-27 18:25:57 +00001161 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
reed@google.com4bbdeac2013-01-24 21:03:11 +00001162 SkRect cullRect;
halcanary96fcdcc2015-08-27 07:41:13 -07001163 const SkRect* cullRectPtr = nullptr;
reed@google.com4bbdeac2013-01-24 21:03:11 +00001164 if (this->computeConservativeLocalClipBounds(&cullRect)) {
1165 cullRectPtr = &cullRect;
1166 }
reed05d90442015-02-12 13:35:52 -08001167 doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr,
caryclark1a7eb262016-01-21 07:07:02 -08001168 ComputeResScaleForStroking(*fMatrix));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 pathPtr = &tmpPath;
1170 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001171
reed@google.com32e5d972011-07-27 18:25:57 +00001172 if (paint->getRasterizer()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 SkMask mask;
reed@google.com32e5d972011-07-27 18:25:57 +00001174 if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
reed@google.com045e62d2011-10-24 12:19:46 +00001175 &fRC->getBounds(), paint->getMaskFilter(), &mask,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
reed@google.com32e5d972011-07-27 18:25:57 +00001177 this->drawDevMask(mask, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 SkMask::FreeImage(mask.fImage);
1179 }
1180 return;
1181 }
1182
1183 // avoid possibly allocating a new path in transform if we can
1184 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1185
1186 // transform the path into device space
1187 pathPtr->transform(*matrix, devPathPtr);
1188
reed82595b62016-05-09 17:48:46 -07001189 this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190}
1191
reed82595b62016-05-09 17:48:46 -07001192void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {
reed@google.com900ecf22014-02-20 20:55:37 +00001193 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194
fmalitac7e211a2016-01-07 10:34:46 -08001195 if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) {
reed@google.come1ca7052013-12-17 19:22:07 +00001196 int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
1197 int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198
reed92fc2ae2015-05-22 08:06:21 -07001199 SkAutoPixmapUnlock result;
1200 if (!bitmap.requestLock(&result)) {
reed@google.coma641f3f2012-12-13 22:16:30 +00001201 return;
1202 }
reed92fc2ae2015-05-22 08:06:21 -07001203 const SkPixmap& pmap = result.pixmap();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 SkMask mask;
reed92fc2ae2015-05-22 08:06:21 -07001205 mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 mask.fFormat = SkMask::kA8_Format;
reed92fc2ae2015-05-22 08:06:21 -07001207 mask.fRowBytes = SkToU32(pmap.rowBytes());
1208 // fImage is typed as writable, but in this case it is used read-only
1209 mask.fImage = (uint8_t*)pmap.addr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001210
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 this->drawDevMask(mask, paint);
1212 } else { // need to xform the bitmap first
1213 SkRect r;
1214 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001215
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216 r.set(0, 0,
1217 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1218 fMatrix->mapRect(&r);
1219 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001220
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 // set the mask's bounds to the transformed bitmap-bounds,
1222 // clipped to the actual device
1223 {
1224 SkIRect devBounds;
reed41e010c2015-06-09 12:16:53 -07001225 devBounds.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 // need intersect(l, t, r, b) on irect
1227 if (!mask.fBounds.intersect(devBounds)) {
1228 return;
1229 }
1230 }
reed@android.com543ed932009-04-24 12:43:40 +00001231
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 mask.fFormat = SkMask::kA8_Format;
1233 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001234 size_t size = mask.computeImageSize();
1235 if (0 == size) {
1236 // the mask is too big to allocated, draw nothing
1237 return;
1238 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239
1240 // allocate (and clear) our temp buffer to hold the transformed bitmap
scroggo565901d2015-12-10 10:44:13 -08001241 SkAutoTMalloc<uint8_t> storage(size);
1242 mask.fImage = storage.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001244
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1246 {
1247 SkBitmap device;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001248 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
1249 mask.fImage, mask.fRowBytes);
reed@google.coma76de3d2011-01-13 18:30:42 +00001250
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 SkCanvas c(device);
1252 // need the unclipped top/left for the translate
1253 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1254 -SkIntToScalar(mask.fBounds.fTop));
1255 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001256
1257 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001258 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001259 SkPaint tmpPaint;
1260 tmpPaint.setFlags(paint.getFlags());
brianosman66a96d02016-05-19 08:43:55 -07001261 tmpPaint.setFilterQuality(paint.getFilterQuality());
reed@google.com40c2ba22011-07-25 19:41:22 +00001262 SkAutoBitmapShaderInstall install(bitmap, tmpPaint);
reed@android.com3469c762009-02-24 19:03:20 +00001263 SkRect rr;
1264 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1265 SkIntToScalar(bitmap.height()));
reed@google.com40c2ba22011-07-25 19:41:22 +00001266 c.drawRect(rr, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 }
1268 this->drawDevMask(mask, paint);
1269 }
1270}
1271
reed@google.com045e62d2011-10-24 12:19:46 +00001272static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 const SkRect& srcR) {
1274 SkRect dstR;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 m.mapRect(&dstR, srcR);
reedb07a94f2014-11-19 05:03:18 -08001276 return c.quickReject(dstR.roundOut());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277}
1278
reed@google.com045e62d2011-10-24 12:19:46 +00001279static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 int width, int height) {
1281 SkRect r;
1282 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1283 return clipped_out(matrix, clip, r);
1284}
1285
reedc240e712015-05-23 12:26:41 -07001286static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
1287 return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
reed@google.com045e62d2011-10-24 12:19:46 +00001288}
1289
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed03939122014-12-15 13:42:51 -08001291 const SkRect* dstBounds, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001292 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293
1294 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001295 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001297 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 return;
1299 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001300
fmalitaf85d2a42016-10-03 13:46:58 -07001301 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1302 if (origPaint.getStyle() != SkPaint::kFill_Style) {
1303 paint.writable()->setStyle(SkPaint::kFill_Style);
1304 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001305
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 SkMatrix matrix;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001307 matrix.setConcat(*fMatrix, prematrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308
reed@google.com045e62d2011-10-24 12:19:46 +00001309 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 return;
1311 }
1312
fmalitac7e211a2016-01-07 10:34:46 -08001313 if (bitmap.colorType() != kAlpha_8_SkColorType
fmalitaf85d2a42016-10-03 13:46:58 -07001314 && SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001315 //
1316 // It is safe to call lock pixels now, since we know the matrix is
1317 // (more or less) identity.
1318 //
reedc240e712015-05-23 12:26:41 -07001319 SkAutoPixmapUnlock unlocker;
1320 if (!bitmap.requestLock(&unlocker)) {
reed@google.comf7ef56d2012-12-14 13:46:53 +00001321 return;
1322 }
reedc240e712015-05-23 12:26:41 -07001323 const SkPixmap& pmap = unlocker.pixmap();
reed@google.come1ca7052013-12-17 19:22:07 +00001324 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1325 int iy = SkScalarRoundToInt(matrix.getTranslateY());
reedc240e712015-05-23 12:26:41 -07001326 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001327 SkTBlitterAllocator allocator;
1328 // blitter will be owned by the allocator.
fmalitaf85d2a42016-10-03 13:46:58 -07001329 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator);
reed@google.com045e62d2011-10-24 12:19:46 +00001330 if (blitter) {
reedc240e712015-05-23 12:26:41 -07001331 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
1332 *fRC, blitter);
reed@google.com045e62d2011-10-24 12:19:46 +00001333 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 }
reedc240e712015-05-23 12:26:41 -07001335 // if !blitter, then we fall-through to the slower case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001338
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 // now make a temp draw on the stack, and use it
1340 //
1341 SkDraw draw(*this);
1342 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001343
Matt Sarett70ac8a92016-11-03 10:55:08 -04001344 if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
fmalitaf85d2a42016-10-03 13:46:58 -07001345 draw.drawBitmapAsMask(bitmap, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 } else {
fmalitaf85d2a42016-10-03 13:46:58 -07001347 SkAutoBitmapShaderInstall install(bitmap, *paint);
reed03939122014-12-15 13:42:51 -08001348 const SkPaint& paintWithShader = install.paintWithShader();
1349 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1350 if (dstBounds) {
1351 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1352 } else {
1353 draw.drawRect(srcBounds, paintWithShader);
1354 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 }
1356}
1357
reedc240e712015-05-23 12:26:41 -07001358void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001359 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001360
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361 // nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001362 if (fRC->isEmpty() ||
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 bitmap.width() == 0 || bitmap.height() == 0 ||
reed@google.com900ecf22014-02-20 20:55:37 +00001364 bitmap.colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365 return;
1366 }
1367
reedc240e712015-05-23 12:26:41 -07001368 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369
reed@google.com045e62d2011-10-24 12:19:46 +00001370 if (fRC->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 return; // nothing to draw
1372 }
1373
reed@google.com40c2ba22011-07-25 19:41:22 +00001374 SkPaint paint(origPaint);
1375 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376
reedc240e712015-05-23 12:26:41 -07001377 SkAutoPixmapUnlock unlocker;
1378 if (!bitmap.requestLock(&unlocker)) {
1379 return;
1380 }
1381 const SkPixmap& pmap = unlocker.pixmap();
1382
halcanary96fcdcc2015-08-27 07:41:13 -07001383 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +00001384 SkTBlitterAllocator allocator;
1385 // blitter will be owned by the allocator.
reed41e010c2015-06-09 12:16:53 -07001386 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 if (blitter) {
reed@google.com045e62d2011-10-24 12:19:46 +00001388 SkScan::FillIRect(bounds, *fRC, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 return;
1390 }
1391 }
1392
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 SkMatrix matrix;
1394 SkRect r;
1395
1396 // get a scalar version of our rect
1397 r.set(bounds);
1398
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001399 // create shader with offset
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 matrix.setTranslate(r.fLeft, r.fTop);
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001401 SkAutoBitmapShaderInstall install(bitmap, paint, &matrix);
1402 const SkPaint& shaderPaint = install.paintWithShader();
reed@google.coma76de3d2011-01-13 18:30:42 +00001403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 SkDraw draw(*this);
1405 matrix.reset();
1406 draw.fMatrix = &matrix;
1407 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001408 // is this OK if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001409 draw.drawRect(r, shaderPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410}
1411
1412///////////////////////////////////////////////////////////////////////////////
1413
1414#include "SkScalerContext.h"
1415#include "SkGlyphCache.h"
reed@google.come6913762012-08-07 15:19:47 +00001416#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417#include "SkUtils.h"
1418
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001419bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
1420 // hairline glyphs are fast enough so we don't need to cache them
1421 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
1422 return true;
1423 }
1424
1425 // we don't cache perspective
1426 if (ctm.hasPerspective()) {
1427 return true;
1428 }
1429
1430 SkMatrix textM;
1431 return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM));
1432}
1433
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
1435 SkScalar x, SkScalar y,
1436 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001437 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438
djsollen@google.com166e6532012-03-20 14:24:38 +00001439 SkTextToPathIter iter(text, byteLength, paint, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440
1441 SkMatrix matrix;
1442 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1443 matrix.postTranslate(x, y);
1444
1445 const SkPath* iterPath;
1446 SkScalar xpos, prevXPos = 0;
1447
reed@google.com7b4531f2012-08-07 15:53:00 +00001448 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001450 if (iterPath) {
1451 const SkPaint& pnt = iter.getPaint();
1452 if (fDevice) {
1453 fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
1454 } else {
1455 this->drawPath(*iterPath, pnt, &matrix, false);
1456 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 prevXPos = xpos;
1459 }
1460}
1461
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462// disable warning : local variable used without having been initialized
bungemand7dc76f2016-03-10 11:14:40 -08001463#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464#pragma warning ( push )
1465#pragma warning ( disable : 4701 )
1466#endif
1467
herbf553e4e2015-11-09 08:51:56 -08001468////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469
herbd4c24f62015-12-07 12:12:29 -08001470class DrawOneGlyph {
1471public:
1472 DrawOneGlyph(const SkDraw& draw, const SkPaint& paint, SkGlyphCache* cache, SkBlitter* blitter)
1473 : fUseRegionToDraw(UsingRegionToDraw(draw.fRC))
1474 , fGlyphCache(cache)
1475 , fBlitter(blitter)
1476 , fClip(fUseRegionToDraw ? &draw.fRC->bwRgn() : nullptr)
1477 , fDraw(draw)
1478 , fPaint(paint)
1479 , fClipBounds(PickClipBounds(draw)) { }
herb11a7f7f2015-11-24 12:41:00 -08001480
herbd4c24f62015-12-07 12:12:29 -08001481 void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
1482 position += rounding;
herbd4c24f62015-12-07 12:12:29 -08001483 // Prevent glyphs from being drawn outside of or straddling the edge of device space.
mtklein875e13c2016-06-19 05:28:33 -07001484 // Comparisons written a little weirdly so that NaN coordinates are treated safely.
1485 auto gt = [](float a, int b) { return !(a <= (float)b); };
1486 auto lt = [](float a, int b) { return !(a >= (float)b); };
1487 if (gt(position.fX, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1488 lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
1489 gt(position.fY, INT_MAX - (INT16_MAX + UINT16_MAX)) ||
1490 lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))) {
herbd4c24f62015-12-07 12:12:29 -08001491 return;
1492 }
herb11a7f7f2015-11-24 12:41:00 -08001493
benjaminwagner77a9cc12016-03-25 11:24:30 -07001494 int left = SkScalarFloorToInt(position.fX);
1495 int top = SkScalarFloorToInt(position.fY);
herbd4c24f62015-12-07 12:12:29 -08001496 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1497
1498 left += glyph.fLeft;
1499 top += glyph.fTop;
1500
1501 int right = left + glyph.fWidth;
1502 int bottom = top + glyph.fHeight;
1503
1504 SkMask mask;
1505 mask.fBounds.set(left, top, right, bottom);
mtklein875e13c2016-06-19 05:28:33 -07001506 SkASSERT(!mask.fBounds.isEmpty());
herbd4c24f62015-12-07 12:12:29 -08001507
1508 if (fUseRegionToDraw) {
1509 SkRegion::Cliperator clipper(*fClip, mask.fBounds);
1510
1511 if (!clipper.done() && this->getImageData(glyph, &mask)) {
1512 const SkIRect& cr = clipper.rect();
1513 do {
1514 this->blitMask(mask, cr);
1515 clipper.next();
1516 } while (!clipper.done());
1517 }
1518 } else {
1519 SkIRect storage;
1520 SkIRect* bounds = &mask.fBounds;
1521
1522 // this extra test is worth it, assuming that most of the time it succeeds
1523 // since we can avoid writing to storage
1524 if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) {
1525 if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds))
1526 return;
1527 bounds = &storage;
1528 }
1529
1530 if (this->getImageData(glyph, &mask)) {
1531 this->blitMask(mask, *bounds);
1532 }
1533 }
1534 }
1535
1536private:
1537 static bool UsingRegionToDraw(const SkRasterClip* rClip) {
1538 return rClip->isBW() && !rClip->isRect();
1539 }
1540
1541 static SkIRect PickClipBounds(const SkDraw& draw) {
1542 const SkRasterClip& rasterClip = *draw.fRC;
1543
1544 if (rasterClip.isBW()) {
1545 return rasterClip.bwRgn().getBounds();
1546 } else {
1547 return rasterClip.aaRgn().getBounds();
1548 }
1549 }
1550
1551 bool getImageData(const SkGlyph& glyph, SkMask* mask) {
1552 uint8_t* bits = (uint8_t*)(fGlyphCache->findImage(glyph));
1553 if (nullptr == bits) {
1554 return false; // can't rasterize glyph
1555 }
1556 mask->fImage = bits;
1557 mask->fRowBytes = glyph.rowBytes();
1558 mask->fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1559 return true;
1560 }
1561
herb11a7f7f2015-11-24 12:41:00 -08001562 void blitMask(const SkMask& mask, const SkIRect& clip) const {
1563 if (SkMask::kARGB32_Format == mask.fFormat) {
herbd4c24f62015-12-07 12:12:29 -08001564 SkBitmap bm;
1565 bm.installPixels(
1566 SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()),
1567 (SkPMColor*)mask.fImage, mask.fRowBytes);
1568
1569 fDraw.drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), fPaint);
herb11a7f7f2015-11-24 12:41:00 -08001570 } else {
1571 fBlitter->blitMask(mask, clip);
1572 }
1573 }
1574
herbd4c24f62015-12-07 12:12:29 -08001575 const bool fUseRegionToDraw;
1576 SkGlyphCache * const fGlyphCache;
1577 SkBlitter * const fBlitter;
1578 const SkRegion* const fClip;
1579 const SkDraw& fDraw;
1580 const SkPaint& fPaint;
1581 const SkIRect fClipBounds;
herb11a7f7f2015-11-24 12:41:00 -08001582};
1583
herbd4c24f62015-12-07 12:12:29 -08001584////////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585
brianosmana1e8f8d2016-04-08 06:47:54 -07001586uint32_t SkDraw::scalerContextFlags() const {
1587 uint32_t flags = SkPaint::kBoostContrast_ScalerContextFlag;
Brian Osman7039f742016-11-01 15:56:16 -04001588 if (!fDevice->imageInfo().colorSpace()) {
brianosmana1e8f8d2016-04-08 06:47:54 -07001589 flags |= SkPaint::kFakeGamma_ScalerContextFlag;
1590 }
1591 return flags;
bungemanf6d1e602016-02-22 13:20:28 -08001592}
1593
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594void SkDraw::drawText(const char text[], size_t byteLength,
1595 SkScalar x, SkScalar y, const SkPaint& paint) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001596 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597
reed@android.comf2b98d62010-12-20 18:26:13 +00001598 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599
1600 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001601 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602 return;
1603 }
1604
bsalomon@google.com36d6eda2012-10-10 16:12:14 +00001605 // SkScalarRec doesn't currently have a way of representing hairline stroke and
1606 // will fill if its frame-width is 0.
reed@google.comed43dff2013-06-04 16:56:27 +00001607 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 this->drawText_asPaths(text, byteLength, x, y, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609 return;
1610 }
1611
brianosmana1e8f8d2016-04-08 06:47:54 -07001612 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), fMatrix);
herb11a7f7f2015-11-24 12:41:00 -08001613
1614 // The Blitter Choose needs to be live while using the blitter below.
1615 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1616 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001617 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618
herbe59124e2015-11-18 10:54:39 -08001619 SkFindAndPlaceGlyph::ProcessText(
herb4c11b3f2015-11-20 13:53:12 -08001620 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001621 {x, y}, *fMatrix, paint.getTextAlign(), cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624//////////////////////////////////////////////////////////////////////////////
1625
reed@google.comed43dff2013-06-04 16:56:27 +00001626void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -07001627 const SkScalar pos[], int scalarsPerPosition,
1628 const SkPoint& offset, const SkPaint& origPaint) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001629 // setup our std paint, in hopes of getting hits in the cache
1630 SkPaint paint(origPaint);
1631 SkScalar matrixScale = paint.setupForAsPaths();
1632
reed@google.com5a649022013-06-05 18:00:30 +00001633 SkMatrix matrix;
1634 matrix.setScale(matrixScale, matrixScale);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001635
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001636 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
1637 paint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07001638 paint.setPathEffect(nullptr);
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001639
robertphillipse34f17d2016-07-19 07:59:22 -07001640 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
1641 paint.isDevKernText(),
1642 true);
brianosmana1e8f8d2016-04-08 06:47:54 -07001643 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), nullptr);
reed@google.comed43dff2013-06-04 16:56:27 +00001644
1645 const char* stop = text + byteLength;
bungeman79738cc2015-03-11 14:05:29 -07001646 SkTextAlignProc alignProc(paint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -07001647 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001648
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001649 // Now restore the original settings, so we "draw" with whatever style/stroking.
1650 paint.setStyle(origPaint.getStyle());
Mike Reed693fdbd2017-01-12 10:13:40 -05001651 paint.setPathEffect(origPaint.refPathEffect());
commit-bot@chromium.org4e82cdb2014-05-23 12:32:23 +00001652
reed@google.comed43dff2013-06-04 16:56:27 +00001653 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -08001654 const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
reed@google.comed43dff2013-06-04 16:56:27 +00001655 if (glyph.fWidth) {
1656 const SkPath* path = cache->findPath(glyph);
1657 if (path) {
kkinnunencb9a2c82014-06-12 23:06:28 -07001658 SkPoint tmsLoc;
1659 tmsProc(pos, &tmsLoc);
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001660 SkPoint loc;
kkinnunencb9a2c82014-06-12 23:06:28 -07001661 alignProc(tmsLoc, glyph, &loc);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001662
bungeman@google.comcfd90d62013-12-13 21:26:02 +00001663 matrix[SkMatrix::kMTransX] = loc.fX;
1664 matrix[SkMatrix::kMTransY] = loc.fY;
reed@google.com5a649022013-06-05 18:00:30 +00001665 if (fDevice) {
1666 fDevice->drawPath(*this, *path, paint, &matrix, false);
1667 } else {
1668 this->drawPath(*path, paint, &matrix, false);
1669 }
reed@google.comed43dff2013-06-04 16:56:27 +00001670 }
1671 }
1672 pos += scalarsPerPosition;
1673 }
1674}
1675
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676void SkDraw::drawPosText(const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -07001677 const SkScalar pos[], int scalarsPerPosition,
1678 const SkPoint& offset, const SkPaint& paint) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001679 SkASSERT(byteLength == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1681
reed@android.comf2b98d62010-12-20 18:26:13 +00001682 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683
1684 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07001685 if (text == nullptr || byteLength == 0 || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 return;
1687 }
1688
reed@google.comed43dff2013-06-04 16:56:27 +00001689 if (ShouldDrawTextAsPaths(paint, *fMatrix)) {
fmalita05c4a432014-09-29 06:29:53 -07001690 this->drawPosText_asPaths(text, byteLength, pos, scalarsPerPosition, offset, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691 return;
1692 }
1693
brianosmana1e8f8d2016-04-08 06:47:54 -07001694 SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->scalerContextFlags(), fMatrix);
herbd4c24f62015-12-07 12:12:29 -08001695
herb9be5ff62015-11-11 11:30:11 -08001696 // The Blitter Choose needs to be live while using the blitter below.
herb11a7f7f2015-11-24 12:41:00 -08001697 SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
1698 SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
bungemanf6d1e602016-02-22 13:20:28 -08001699 DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
herbd4c24f62015-12-07 12:12:29 -08001700 SkPaint::Align textAlignment = paint.getTextAlign();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701
herb9be5ff62015-11-11 11:30:11 -08001702 SkFindAndPlaceGlyph::ProcessPosText(
herb4c11b3f2015-11-20 13:53:12 -08001703 paint.getTextEncoding(), text, byteLength,
bungemanf6d1e602016-02-22 13:20:28 -08001704 offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache.get(), drawOneGlyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705}
1706
bungemand7dc76f2016-03-10 11:14:40 -08001707#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708#pragma warning ( pop )
1709#endif
1710
1711///////////////////////////////////////////////////////////////////////////////
1712
reed5dc6b7d2015-04-14 10:40:44 -07001713static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
1715}
1716
1717static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
1718 const SkPoint texs[], SkMatrix* matrix) {
1719 SkPoint src[3], dst[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00001720
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721 src[0] = texs[state.f0];
1722 src[1] = texs[state.f1];
1723 src[2] = texs[state.f2];
1724 dst[0] = verts[state.f0];
1725 dst[1] = verts[state.f1];
1726 dst[2] = verts[state.f2];
1727 return matrix->setPolyToPoly(src, dst, 3);
1728}
1729
1730class SkTriColorShader : public SkShader {
1731public:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001732 SkTriColorShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001734 class TriColorShaderContext : public SkShader::Context {
1735 public:
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001736 TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001737 virtual ~TriColorShaderContext();
mtklein36352bf2015-03-25 18:17:31 -07001738 void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001739
1740 private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001741 bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
1742
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001743 SkMatrix fDstToUnit;
1744 SkPMColor fColors[3];
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001745 bool fSetup;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001746
1747 typedef SkShader::Context INHERITED;
1748 };
reed@google.coma76de3d2011-01-13 18:30:42 +00001749
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001750 struct TriColorShaderData {
1751 const SkPoint* pts;
1752 const SkColor* colors;
1753 const VertState *state;
1754 };
1755
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001756 SK_TO_STRING_OVERRIDE()
mtklein7e44bb12015-01-07 09:06:08 -08001757
1758 // For serialization. This will never be called.
halcanary96fcdcc2015-08-27 07:41:13 -07001759 Factory getFactory() const override { sk_throw(); return nullptr; }
djsollen@google.comba28d032012-03-26 17:57:35 +00001760
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001761 // Supply setup data to context from drawing setup
1762 void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; }
1763
1764 // Take the setup data from context when needed.
1765 TriColorShaderData* takeSetupData() {
1766 TriColorShaderData *data = fSetupData;
1767 fSetupData = NULL;
1768 return data;
1769 }
1770
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771protected:
reed773ceda2016-03-03 18:18:25 -08001772 size_t onContextSize(const ContextRec&) const override;
mtklein36352bf2015-03-25 18:17:31 -07001773 Context* onCreateContext(const ContextRec& rec, void* storage) const override {
halcanary385fe4d2015-08-26 13:07:48 -07001774 return new (storage) TriColorShaderContext(*this, rec);
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +00001775 }
1776
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777private:
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001778 TriColorShaderData *fSetupData;
1779
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780 typedef SkShader INHERITED;
1781};
1782
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001783bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[],
1784 int index0, int index1, int index2) {
reed@google.coma76de3d2011-01-13 18:30:42 +00001785
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 fColors[0] = SkPreMultiplyColor(colors[index0]);
1787 fColors[1] = SkPreMultiplyColor(colors[index1]);
1788 fColors[2] = SkPreMultiplyColor(colors[index2]);
reed@google.coma76de3d2011-01-13 18:30:42 +00001789
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790 SkMatrix m, im;
1791 m.reset();
1792 m.set(0, pts[index1].fX - pts[index0].fX);
1793 m.set(1, pts[index2].fX - pts[index0].fX);
1794 m.set(2, pts[index0].fX);
1795 m.set(3, pts[index1].fY - pts[index0].fY);
1796 m.set(4, pts[index2].fY - pts[index0].fY);
1797 m.set(5, pts[index0].fY);
1798 if (!m.invert(&im)) {
1799 return false;
1800 }
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001801 // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix
1802 // as our interators are intrinsically tied to the vertices, and nothing else.
1803 SkMatrix ctmInv;
1804 if (!this->getCTM().invert(&ctmInv)) {
1805 return false;
1806 }
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001807 // TODO replace INV(m) * INV(ctm) with INV(ctm * m)
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001808 fDstToUnit.setConcat(im, ctmInv);
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001809 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810}
1811
1812#include "SkColorPriv.h"
reed@android.com689411a2008-12-18 02:52:32 +00001813#include "SkComposeShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814
1815static int ScalarTo256(SkScalar v) {
benjaminwagner43437c22016-02-24 10:57:47 -08001816 return static_cast<int>(SkScalarPin(v, 0, 1) * 256 + 0.5);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817}
1818
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001819SkTriColorShader::SkTriColorShader()
1820 : INHERITED(NULL)
1821 , fSetupData(NULL) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001822
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +00001823SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader,
1824 const ContextRec& rec)
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001825 : INHERITED(shader, rec)
1826 , fSetup(false) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001827
1828SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {}
1829
reed773ceda2016-03-03 18:18:25 -08001830size_t SkTriColorShader::onContextSize(const ContextRec&) const {
reeda0cee5f2016-03-04 07:38:11 -08001831 return sizeof(TriColorShaderContext);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001832}
reed773ceda2016-03-03 18:18:25 -08001833
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001834void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001835 SkTriColorShader* parent = static_cast<SkTriColorShader*>(const_cast<SkShader*>(&fShader));
1836 TriColorShaderData* set = parent->takeSetupData();
1837 if (set) {
1838 fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2);
1839 }
1840
1841 if (!fSetup) {
1842 // Invalid matrices. Not checked before so no need to assert.
1843 return;
1844 }
1845
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001846 const int alphaScale = Sk255To256(this->getPaintAlpha());
1847
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848 SkPoint src;
reed@google.coma76de3d2011-01-13 18:30:42 +00001849
reed@android.com8a1c16f2008-12-17 15:59:43 +00001850 for (int i = 0; i < count; i++) {
1851 fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
1852 x += 1;
reed@google.coma76de3d2011-01-13 18:30:42 +00001853
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854 int scale1 = ScalarTo256(src.fX);
1855 int scale2 = ScalarTo256(src.fY);
1856 int scale0 = 256 - scale1 - scale2;
1857 if (scale0 < 0) {
1858 if (scale1 > scale2) {
1859 scale2 = 256 - scale1;
1860 } else {
1861 scale1 = 256 - scale2;
1862 }
1863 scale0 = 0;
1864 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001865
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001866 if (256 != alphaScale) {
1867 scale0 = SkAlphaMul(scale0, alphaScale);
1868 scale1 = SkAlphaMul(scale1, alphaScale);
1869 scale2 = SkAlphaMul(scale2, alphaScale);
1870 }
1871
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872 dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
commit-bot@chromium.org06a32062014-05-05 21:35:09 +00001873 SkAlphaMulQ(fColors[1], scale1) +
1874 SkAlphaMulQ(fColors[2], scale2);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875 }
1876}
1877
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001878#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001879void SkTriColorShader::toString(SkString* str) const {
1880 str->append("SkTriColorShader: (");
1881
1882 this->INHERITED::toString(str);
1883
1884 str->append(")");
1885}
1886#endif
1887
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
1889 const SkPoint vertices[], const SkPoint textures[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001890 const SkColor colors[], SkBlendMode bmode,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891 const uint16_t indices[], int indexCount,
1892 const SkPaint& paint) const {
bsalomon49f085d2014-09-05 13:34:00 -07001893 SkASSERT(0 == count || vertices);
reed@google.coma76de3d2011-01-13 18:30:42 +00001894
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 // abort early if there is nothing to draw
reed@google.com045e62d2011-10-24 12:19:46 +00001896 if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001897 return;
1898 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001899
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900 // transform out vertices into device coordinates
1901 SkAutoSTMalloc<16, SkPoint> storage(count);
1902 SkPoint* devVerts = storage.get();
1903 fMatrix->mapPoints(devVerts, vertices, count);
reed@google.coma76de3d2011-01-13 18:30:42 +00001904
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905 /*
1906 We can draw the vertices in 1 of 4 ways:
1907
1908 - solid color (no shader/texture[], no colors[])
1909 - just colors (no shader/texture[], has colors[])
1910 - just texture (has shader/texture[], no colors[])
1911 - colors * texture (has shader/texture[], has colors[])
reed@google.coma76de3d2011-01-13 18:30:42 +00001912
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 Thus for texture drawing, we need both texture[] and a shader.
1914 */
1915
reed8a21c9f2016-03-08 18:50:00 -08001916 auto triShader = sk_make_sp<SkTriColorShader>();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917 SkPaint p(paint);
1918
1919 SkShader* shader = p.getShader();
halcanary96fcdcc2015-08-27 07:41:13 -07001920 if (nullptr == shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921 // if we have no shader, we ignore the texture coordinates
halcanary96fcdcc2015-08-27 07:41:13 -07001922 textures = nullptr;
1923 } else if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924 // if we don't have texture coordinates, ignore the shader
halcanary96fcdcc2015-08-27 07:41:13 -07001925 p.setShader(nullptr);
1926 shader = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 }
1928
1929 // setup the custom shader (if needed)
bsalomon49f085d2014-09-05 13:34:00 -07001930 if (colors) {
halcanary96fcdcc2015-08-27 07:41:13 -07001931 if (nullptr == textures) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932 // just colors (no texture)
reed8a21c9f2016-03-08 18:50:00 -08001933 p.setShader(triShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934 } else {
1935 // colors * texture
1936 SkASSERT(shader);
Mike Reed7d954ad2016-10-28 15:42:34 -04001937 p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), bmode));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 }
1939 }
1940
reed41e010c2015-06-09 12:16:53 -07001941 SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00001942 // Abort early if we failed to create a shader context.
reed@google.comea033602012-12-14 13:13:55 +00001943 if (blitter->isNullBlitter()) {
1944 return;
1945 }
1946
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 // setup our state and function pointer for iterating triangles
1948 VertState state(count, indices, indexCount);
1949 VertState::Proc vertProc = state.chooseProc(vmode);
reed@google.coma76de3d2011-01-13 18:30:42 +00001950
bsalomon49f085d2014-09-05 13:34:00 -07001951 if (textures || colors) {
aleksandar.stojiljkovic88cb8222016-03-08 11:18:21 -08001952 SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state };
1953
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954 while (vertProc(&state)) {
bsalomon49f085d2014-09-05 13:34:00 -07001955 if (textures) {
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001956 SkMatrix tempM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957 if (texture_to_matrix(state, vertices, textures, &tempM)) {
fmalitad0c4e092016-02-22 17:19:04 -08001958 SkShader::ContextRec rec(p, *fMatrix, &tempM,
Brian Osman11970e52016-12-05 15:26:50 -05001959 SkBlitter::PreferredShaderDest(fDst.info()),
1960 fDst.colorSpace());
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +00001961 if (!blitter->resetShaderContext(rec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962 continue;
1963 }
1964 }
1965 }
bsalomon49f085d2014-09-05 13:34:00 -07001966 if (colors) {
reed8a21c9f2016-03-08 18:50:00 -08001967 triShader->bindSetupData(&verticesSetup);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968 }
reed@google.com045e62d2011-10-24 12:19:46 +00001969
1970 SkPoint tmp[] = {
1971 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
1972 };
1973 SkScan::FillTriangle(tmp, *fRC, blitter.get());
reed8a21c9f2016-03-08 18:50:00 -08001974 triShader->bindSetupData(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976 } else {
commit-bot@chromium.org559a8832014-05-30 10:08:22 +00001977 // no colors[] and no texture, stroke hairlines with paint's color.
reed5dc6b7d2015-04-14 10:40:44 -07001978 SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
reed@google.com045e62d2011-10-24 12:19:46 +00001979 const SkRasterClip& clip = *fRC;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980 while (vertProc(&state)) {
reed5dc6b7d2015-04-14 10:40:44 -07001981 SkPoint array[] = {
1982 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
1983 };
1984 hairProc(array, 4, clip, blitter.get());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985 }
1986 }
1987}
1988
reed@google.com0a0a2362011-03-23 13:51:55 +00001989///////////////////////////////////////////////////////////////////////////////
1990///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991
1992#ifdef SK_DEBUG
1993
reed@android.comf2b98d62010-12-20 18:26:13 +00001994void SkDraw::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -07001995 SkASSERT(fMatrix != nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -07001996 SkASSERT(fRC != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997
reed@google.com045e62d2011-10-24 12:19:46 +00001998 const SkIRect& cr = fRC->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999 SkIRect br;
2000
reed41e010c2015-06-09 12:16:53 -07002001 br.set(0, 0, fDst.width(), fDst.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002 SkASSERT(cr.isEmpty() || br.contains(cr));
2003}
2004
2005#endif
2006
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007////////////////////////////////////////////////////////////////////////////////////////////////
2008
2009#include "SkPath.h"
2010#include "SkDraw.h"
2011#include "SkRegion.h"
2012#include "SkBlitter.h"
2013
2014static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
reedb07a94f2014-11-19 05:03:18 -08002015 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 SkIRect* bounds) {
2017 if (devPath.isEmpty()) {
2018 return false;
2019 }
2020
reed@android.com8a1c16f2008-12-17 15:59:43 +00002021 // init our bounds from the path
reed11fa2242015-03-13 06:08:28 -07002022 *bounds = devPath.getBounds().makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
reed@google.coma76de3d2011-01-13 18:30:42 +00002023
tomhudson@google.com6db75fc2012-03-23 14:46:48 +00002024 SkIPoint margin = SkIPoint::Make(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002025 if (filter) {
2026 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00002027
bungeman@google.com5af16f82011-09-02 15:06:44 +00002028 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00002029
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030 srcM.fBounds = *bounds;
2031 srcM.fFormat = SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
2033 return false;
2034 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 }
2036
bungeman@google.com5af16f82011-09-02 15:06:44 +00002037 // (possibly) trim the bounds to reflect the clip
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038 // (plus whatever slop the filter needs)
bungeman@google.com5af16f82011-09-02 15:06:44 +00002039 if (clipBounds) {
reed@android.com35555912009-03-16 18:46:55 +00002040 // Ugh. Guard against gigantic margins from wacky filters. Without this
2041 // check we can request arbitrary amounts of slop beyond our visible
2042 // clip, and bring down the renderer (at least on finite RAM machines
2043 // like handsets, etc.). Need to balance this invented value between
2044 // quality of large filters like blurs, and the corresponding memory
2045 // requests.
2046 static const int MAX_MARGIN = 128;
reed11fa2242015-03-13 06:08:28 -07002047 if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
2048 SkMin32(margin.fY, MAX_MARGIN)))) {
bungeman@google.com5af16f82011-09-02 15:06:44 +00002049 return false;
2050 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051 }
2052
2053 return true;
2054}
2055
bsalomon055e1922016-05-06 07:22:58 -07002056static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
2057 SkStrokeRec::InitStyle style) {
reed41e010c2015-06-09 12:16:53 -07002058 SkDraw draw;
2059 if (!draw.fDst.reset(mask)) {
2060 return;
2061 }
2062
reed@google.com045e62d2011-10-24 12:19:46 +00002063 SkRasterClip clip;
2064 SkMatrix matrix;
2065 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066
reed@google.com045e62d2011-10-24 12:19:46 +00002067 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
2069 -SkIntToScalar(mask.fBounds.fTop));
2070
reed@google.com045e62d2011-10-24 12:19:46 +00002071 draw.fRC = &clip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 draw.fMatrix = &matrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 paint.setAntiAlias(true);
bsalomon055e1922016-05-06 07:22:58 -07002074 switch (style) {
2075 case SkStrokeRec::kHairline_InitStyle:
2076 SkASSERT(!paint.getStrokeWidth());
2077 paint.setStyle(SkPaint::kStroke_Style);
2078 break;
2079 case SkStrokeRec::kFill_InitStyle:
2080 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
2081 break;
2082
2083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084 draw.drawPath(devPath, paint);
2085}
2086
2087bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +00002088 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002089 SkMask* mask, SkMask::CreateMode mode,
bsalomon055e1922016-05-06 07:22:58 -07002090 SkStrokeRec::InitStyle style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091 if (SkMask::kJustRenderImage_CreateMode != mode) {
2092 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
2093 return false;
2094 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002095
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
2097 mask->fFormat = SkMask::kA8_Format;
2098 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00002099 size_t size = mask->computeImageSize();
2100 if (0 == size) {
2101 // we're too big to allocate the mask, abort
2102 return false;
2103 }
2104 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105 memset(mask->fImage, 0, mask->computeImageSize());
2106 }
2107
2108 if (SkMask::kJustComputeBounds_CreateMode != mode) {
junov@chromium.org2ac4ef52012-04-04 15:16:51 +00002109 draw_into_mask(*mask, devPath, style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002110 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002111
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 return true;
2113}