blob: 24d9506bd79ca2719440e3f0f16cd09fb833f0ca [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070022#include "SkLatticeIter.h"
reed262a71b2015-12-05 13:07:27 -080023#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000024#include "SkMetaData.h"
reedc83a2972015-07-16 07:40:45 -070025#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070026#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000028#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080029#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000030#include "SkRRect.h"
vjiaoblack904527d2016-08-09 09:32:09 -070031#include "SkShadowPaintFilterCanvas.h"
32#include "SkShadowShader.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000033#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080034#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000035#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070036#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000037#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000038#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080039#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070040
41#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080044#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#include "GrRenderTarget.h"
bsalomon614d8f92016-07-13 15:42:40 -070046#include "SkGrPriv.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000047#endif
48
reede3b38ce2016-01-08 09:18:44 -080049#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
50
reed2d1afab2016-06-29 14:33:11 -070051//#define SK_SUPPORT_PRECHECK_CLIPRECT
52
reedc83a2972015-07-16 07:40:45 -070053/*
54 * Return true if the drawing this rect would hit every pixels in the canvas.
55 *
56 * Returns false if
57 * - rect does not contain the canvas' bounds
58 * - paint is not fill
59 * - paint would blur or otherwise change the coverage of the rect
60 */
61bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
62 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070063 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
64 (int)kNone_ShaderOverrideOpacity,
65 "need_matching_enums0");
66 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
67 (int)kOpaque_ShaderOverrideOpacity,
68 "need_matching_enums1");
69 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
70 (int)kNotOpaque_ShaderOverrideOpacity,
71 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070072
73 const SkISize size = this->getBaseLayerSize();
74 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
75 if (!this->getClipStack()->quickContains(bounds)) {
76 return false;
77 }
78
79 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070080 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070081 return false; // conservative
82 }
halcanaryc5769b22016-08-10 07:13:21 -070083
84 SkRect devRect;
85 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
86 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070087 return false;
88 }
89 }
90
91 if (paint) {
92 SkPaint::Style paintStyle = paint->getStyle();
93 if (!(paintStyle == SkPaint::kFill_Style ||
94 paintStyle == SkPaint::kStrokeAndFill_Style)) {
95 return false;
96 }
97 if (paint->getMaskFilter() || paint->getLooper()
98 || paint->getPathEffect() || paint->getImageFilter()) {
99 return false; // conservative
100 }
101 }
102 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
103}
104
105///////////////////////////////////////////////////////////////////////////////////////////////////
106
reedd990e2f2014-12-22 11:58:30 -0800107static bool gIgnoreSaveLayerBounds;
108void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
109 gIgnoreSaveLayerBounds = ignore;
110}
111bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
112 return gIgnoreSaveLayerBounds;
113}
114
reed0acf1b42014-12-22 16:12:38 -0800115static bool gTreatSpriteAsBitmap;
116void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
117 gTreatSpriteAsBitmap = spriteAsBitmap;
118}
119bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
120 return gTreatSpriteAsBitmap;
121}
122
reed@google.comda17f752012-08-16 18:27:05 +0000123// experimental for faster tiled drawing...
124//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125//#define SK_TRACE_SAVERESTORE
126
127#ifdef SK_TRACE_SAVERESTORE
128 static int gLayerCounter;
129 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
130 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
131
132 static int gRecCounter;
133 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
134 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
135
136 static int gCanvasCounter;
137 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
138 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
139#else
140 #define inc_layer()
141 #define dec_layer()
142 #define inc_rec()
143 #define dec_rec()
144 #define inc_canvas()
145 #define dec_canvas()
146#endif
147
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000148typedef SkTLazy<SkPaint> SkLazyPaint;
149
reedc83a2972015-07-16 07:40:45 -0700150void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000151 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700152 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
153 ? SkSurface::kDiscard_ContentChangeMode
154 : SkSurface::kRetain_ContentChangeMode);
155 }
156}
157
158void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
159 ShaderOverrideOpacity overrideOpacity) {
160 if (fSurfaceBase) {
161 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
162 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
163 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
164 // and therefore we don't care which mode we're in.
165 //
166 if (fSurfaceBase->outstandingImageSnapshot()) {
167 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
168 mode = SkSurface::kDiscard_ContentChangeMode;
169 }
170 }
171 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000172 }
173}
174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
reed4a8126e2014-09-22 07:29:03 -0700177static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
178 const uint32_t propFlags = props.flags();
179 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
180 flags &= ~SkPaint::kDither_Flag;
181 }
182 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
183 flags &= ~SkPaint::kAntiAlias_Flag;
184 }
185 return flags;
186}
187
188///////////////////////////////////////////////////////////////////////////////
189
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000190/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 The clip/matrix/proc are fields that reflect the top of the save/restore
192 stack. Whenever the canvas changes, it marks a dirty flag, and then before
193 these are used (assuming we're not on a layer) we rebuild these cache
194 values: they reflect the top of the save stack, but translated and clipped
195 by the device's XY offset and bitmap-bounds.
196*/
197struct DeviceCM {
198 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000199 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000200 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000201 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700202 const SkMatrix* fMatrix;
203 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700204 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205
reed96e657d2015-03-10 17:30:07 -0700206 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700207 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700208 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700209 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700210 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700211 {
reed2c9e2002016-07-25 08:05:22 -0700212 SkSafeRef(device);
reed@google.com4b226022011-01-11 18:32:13 +0000213 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700214 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000215 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000217 ~DeviceCM() {
reed2c9e2002016-07-25 08:05:22 -0700218 SkSafeUnref(fDevice);
halcanary385fe4d2015-08-26 13:07:48 -0700219 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000220 }
reed@google.com4b226022011-01-11 18:32:13 +0000221
mtkleinfeaadee2015-04-08 11:25:48 -0700222 void reset(const SkIRect& bounds) {
223 SkASSERT(!fPaint);
224 SkASSERT(!fNext);
225 SkASSERT(fDevice);
226 fClip.setRect(bounds);
227 }
228
reed@google.com045e62d2011-10-24 12:19:46 +0000229 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
230 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000231 int x = fDevice->getOrigin().x();
232 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 int width = fDevice->width();
234 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 if ((x | y) == 0) {
237 fMatrix = &totalMatrix;
238 fClip = totalClip;
239 } else {
240 fMatrixStorage = totalMatrix;
241 fMatrixStorage.postTranslate(SkIntToScalar(-x),
242 SkIntToScalar(-y));
243 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000244
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 totalClip.translate(-x, -y, &fClip);
246 }
247
reed@google.com045e62d2011-10-24 12:19:46 +0000248 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249
250 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000253 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 SkRegion::kDifference_Op);
255 }
reed@google.com4b226022011-01-11 18:32:13 +0000256
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000257 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259#ifdef SK_DEBUG
260 if (!fClip.isEmpty()) {
261 SkIRect deviceR;
262 deviceR.set(0, 0, width, height);
263 SkASSERT(deviceR.contains(fClip.getBounds()));
264 }
265#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000266 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267};
268
269/* This is the record we keep for each save/restore level in the stack.
270 Since a level optionally copies the matrix and/or stack, we have pointers
271 for these fields. If the value is copied for this level, the copy is
272 stored in the ...Storage field, and the pointer points to that. If the
273 value is not copied for this level, we ignore ...Storage, and just point
274 at the corresponding value in the previous level in the stack.
275*/
276class SkCanvas::MCRec {
277public:
reed1f836ee2014-07-07 07:49:34 -0700278 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700279 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 /* If there are any layers in the stack, this points to the top-most
281 one that is at or below this level in the stack (so we know what
282 bitmap/device to draw into from this level. This value is NOT
283 reference counted, since the real owner is either our fLayer field,
284 or a previous one in a lower level.)
285 */
reed2ff1fce2014-12-11 07:07:37 -0800286 DeviceCM* fTopLayer;
287 SkRasterClip fRasterClip;
288 SkMatrix fMatrix;
289 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290
vjiaoblacke5de1302016-07-13 14:05:28 -0700291 // This is the current cumulative depth (aggregate of all done translateZ calls)
292 SkScalar fCurDrawDepth;
293
reedd9544982014-09-09 18:46:22 -0700294 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700295 fFilter = nullptr;
296 fLayer = nullptr;
297 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800298 fMatrix.reset();
299 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700300 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700301
reedd9544982014-09-09 18:46:22 -0700302 // don't bother initializing fNext
303 inc_rec();
304 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700305 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
306 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700307 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700308 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700309 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800310 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 // don't bother initializing fNext
313 inc_rec();
314 }
315 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000316 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700317 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 dec_rec();
319 }
mtkleinfeaadee2015-04-08 11:25:48 -0700320
321 void reset(const SkIRect& bounds) {
322 SkASSERT(fLayer);
323 SkASSERT(fDeferredSaveCount == 0);
324
325 fMatrix.reset();
326 fRasterClip.setRect(bounds);
327 fLayer->reset(bounds);
328 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329};
330
331class SkDrawIter : public SkDraw {
332public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000333 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000334 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000335 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 canvas->updateDeviceCMCache();
337
reed687fa1c2015-04-07 08:00:56 -0700338 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000340 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 }
reed@google.com4b226022011-01-11 18:32:13 +0000342
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 bool next() {
344 // skip over recs with empty clips
345 if (fSkipEmptyClips) {
346 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
347 fCurrLayer = fCurrLayer->fNext;
348 }
349 }
350
reed@google.comf68c5e22012-02-24 16:38:58 +0000351 const DeviceCM* rec = fCurrLayer;
352 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000355 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700357 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700358 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700359 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000361 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362
363 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700364 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000365
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 return true;
367 }
368 return false;
369 }
reed@google.com4b226022011-01-11 18:32:13 +0000370
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000371 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700372 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000373 int getX() const { return fDevice->getOrigin().x(); }
374 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000377
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378private:
379 SkCanvas* fCanvas;
380 const DeviceCM* fCurrLayer;
381 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 SkBool8 fSkipEmptyClips;
383
384 typedef SkDraw INHERITED;
385};
386
387/////////////////////////////////////////////////////////////////////////////
388
reeddbc3cef2015-04-29 12:18:57 -0700389static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
390 return lazy->isValid() ? lazy->get() : lazy->set(orig);
391}
392
393/**
394 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700395 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700396 */
reedd053ce92016-03-22 10:17:23 -0700397static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700398 SkImageFilter* imgf = paint.getImageFilter();
399 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700400 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700401 }
402
reedd053ce92016-03-22 10:17:23 -0700403 SkColorFilter* imgCFPtr;
404 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700405 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700406 }
reedd053ce92016-03-22 10:17:23 -0700407 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700408
409 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700410 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700411 // there is no existing paint colorfilter, so we can just return the imagefilter's
412 return imgCF;
413 }
414
415 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
416 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700417 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700418}
419
senorblanco87e066e2015-10-28 11:23:36 -0700420/**
421 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
422 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
423 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
424 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
425 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
426 * conservative "effective" bounds based on the settings in the paint... with one exception. This
427 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
428 * deliberately ignored.
429 */
430static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
431 const SkRect& rawBounds,
432 SkRect* storage) {
433 SkPaint tmpUnfiltered(paint);
434 tmpUnfiltered.setImageFilter(nullptr);
435 if (tmpUnfiltered.canComputeFastBounds()) {
436 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
437 } else {
438 return rawBounds;
439 }
440}
441
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442class AutoDrawLooper {
443public:
senorblanco87e066e2015-10-28 11:23:36 -0700444 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
445 // paint. It's used to determine the size of the offscreen layer for filters.
446 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700447 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000448 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700449 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000450 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800451#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800453#else
454 fFilter = nullptr;
455#endif
reed4a8126e2014-09-22 07:29:03 -0700456 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000457 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700458 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000459 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460
reedd053ce92016-03-22 10:17:23 -0700461 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700462 if (simplifiedCF) {
463 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700464 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700465 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700466 fPaint = paint;
467 }
468
469 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700470 /**
471 * We implement ImageFilters for a given draw by creating a layer, then applying the
472 * imagefilter to the pixels of that layer (its backing surface/image), and then
473 * we call restore() to xfer that layer to the main canvas.
474 *
475 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
476 * 2. Generate the src pixels:
477 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
478 * return (fPaint). We then draw the primitive (using srcover) into a cleared
479 * buffer/surface.
480 * 3. Restore the layer created in #1
481 * The imagefilter is passed the buffer/surface from the layer (now filled with the
482 * src pixels of the primitive). It returns a new "filtered" buffer, which we
483 * draw onto the previous layer using the xfermode from the original paint.
484 */
reed@google.com8926b162012-03-23 15:36:36 +0000485 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700486 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700487 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700488 SkRect storage;
489 if (rawBounds) {
490 // Make rawBounds include all paint outsets except for those due to image filters.
491 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
492 }
reedbfd5f172016-01-07 11:28:08 -0800493 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700494 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700495 fTempLayerForImageFilter = true;
496 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000497 }
498
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000499 if (SkDrawLooper* looper = paint.getLooper()) {
500 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
501 looper->contextSize());
502 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000503 fIsSimple = false;
504 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700505 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000506 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700507 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000508 }
piotaixrb5fae932014-09-24 13:03:30 -0700509
reed4a8126e2014-09-22 07:29:03 -0700510 uint32_t oldFlags = paint.getFlags();
511 fNewPaintFlags = filter_paint_flags(props, oldFlags);
512 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700513 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700514 paint->setFlags(fNewPaintFlags);
515 fPaint = paint;
516 // if we're not simple, doNext() will take care of calling setFlags()
517 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000518 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000519
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700521 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000522 fCanvas->internalRestore();
523 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000524 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000526
reed@google.com4e2b3d32011-04-07 14:18:59 +0000527 const SkPaint& paint() const {
528 SkASSERT(fPaint);
529 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000531
reed@google.com129ec222012-05-15 13:24:09 +0000532 bool next(SkDrawFilter::Type drawType) {
533 if (fDone) {
534 return false;
535 } else if (fIsSimple) {
536 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000537 return !fPaint->nothingToDraw();
538 } else {
539 return this->doNext(drawType);
540 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000541 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000542
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543private:
reeddbc3cef2015-04-29 12:18:57 -0700544 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
545 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000546 SkCanvas* fCanvas;
547 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000548 SkDrawFilter* fFilter;
549 const SkPaint* fPaint;
550 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700551 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700552 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000553 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000554 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000555 SkDrawLooper::Context* fLooperContext;
556 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000557
558 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559};
560
reed@google.com129ec222012-05-15 13:24:09 +0000561bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700562 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000563 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700564 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000565
reeddbc3cef2015-04-29 12:18:57 -0700566 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
567 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700568 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000569
reed5c476fb2015-04-20 08:04:21 -0700570 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700571 paint->setImageFilter(nullptr);
572 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000573 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000574
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000575 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000576 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000577 return false;
578 }
579 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000580 if (!fFilter->filter(paint, drawType)) {
581 fDone = true;
582 return false;
583 }
halcanary96fcdcc2015-08-27 07:41:13 -0700584 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000585 // no looper means we only draw once
586 fDone = true;
587 }
588 }
589 fPaint = paint;
590
591 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000592 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000593 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000594 }
595
596 // call this after any possible paint modifiers
597 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700598 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000599 return false;
600 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000601 return true;
602}
603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604////////// macros to place around the internal draw calls //////////////////
605
reed262a71b2015-12-05 13:07:27 -0800606#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
607 this->predrawNotify(); \
608 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
609 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
610 SkDrawIter iter(this);
611
612
reed@google.com8926b162012-03-23 15:36:36 +0000613#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000614 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700615 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000616 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000617 SkDrawIter iter(this);
618
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000619#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000620 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700621 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000622 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000624
reedc83a2972015-07-16 07:40:45 -0700625#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
626 this->predrawNotify(bounds, &paint, auxOpaque); \
627 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
628 while (looper.next(type)) { \
629 SkDrawIter iter(this);
630
reed@google.com4e2b3d32011-04-07 14:18:59 +0000631#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632
633////////////////////////////////////////////////////////////////////////////
634
mtkleinfeaadee2015-04-08 11:25:48 -0700635void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
636 this->restoreToCount(1);
637 fCachedLocalClipBounds.setEmpty();
638 fCachedLocalClipBoundsDirty = true;
639 fClipStack->reset();
640 fMCRec->reset(bounds);
641
642 // We're peering through a lot of structs here. Only at this scope do we
643 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
644 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
645}
646
reedd9544982014-09-09 18:46:22 -0700647SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800648 if (device && device->forceConservativeRasterClip()) {
649 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
650 }
651 // Since init() is only called once by our constructors, it is safe to perform this
652 // const-cast.
653 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
654
reed@google.comc0784db2013-12-13 21:16:12 +0000655 fCachedLocalClipBounds.setEmpty();
656 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000657 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000658 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700659 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800660 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700661 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700662#ifdef SK_EXPERIMENTAL_SHADOWING
663 fLights = nullptr;
664#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665
halcanary385fe4d2015-08-26 13:07:48 -0700666 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700667
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700669 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670
reeda499f902015-05-01 09:34:31 -0700671 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
672 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700673 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700674 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700675
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677
halcanary96fcdcc2015-08-27 07:41:13 -0700678 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000679
reedf92c8662014-08-18 08:02:43 -0700680 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700681 // The root device and the canvas should always have the same pixel geometry
682 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700683 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800684 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700685 }
686 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
reed@google.comcde92112011-07-06 20:00:52 +0000689SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000690 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700691 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800692 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000693{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000694 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000695
halcanary96fcdcc2015-08-27 07:41:13 -0700696 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000697}
698
reedd9544982014-09-09 18:46:22 -0700699static SkBitmap make_nopixels(int width, int height) {
700 SkBitmap bitmap;
701 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
702 return bitmap;
703}
704
705class SkNoPixelsBitmapDevice : public SkBitmapDevice {
706public:
robertphillipsfcf78292015-06-19 11:49:52 -0700707 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
708 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800709 {
710 this->setOrigin(bounds.x(), bounds.y());
711 }
reedd9544982014-09-09 18:46:22 -0700712
713private:
piotaixrb5fae932014-09-24 13:03:30 -0700714
reedd9544982014-09-09 18:46:22 -0700715 typedef SkBitmapDevice INHERITED;
716};
717
reed96a857e2015-01-25 10:33:58 -0800718SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000719 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800720 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800721 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000722{
723 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700724
halcanary385fe4d2015-08-26 13:07:48 -0700725 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
726 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700727}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000728
reed78e27682014-11-19 08:04:34 -0800729SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700730 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700731 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800732 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700733{
734 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700735
halcanary385fe4d2015-08-26 13:07:48 -0700736 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700737}
738
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000739SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000740 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700741 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800742 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000743{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700745
reedd9544982014-09-09 18:46:22 -0700746 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747}
748
robertphillipsfcf78292015-06-19 11:49:52 -0700749SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
750 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700751 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800752 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700753{
754 inc_canvas();
755
756 this->init(device, flags);
757}
758
reed4a8126e2014-09-22 07:29:03 -0700759SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700760 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700761 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800762 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700763{
764 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700765
halcanary385fe4d2015-08-26 13:07:48 -0700766 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700767 this->init(device, kDefault_InitFlags);
768}
reed29c857d2014-09-21 10:25:07 -0700769
reed4a8126e2014-09-22 07:29:03 -0700770SkCanvas::SkCanvas(const SkBitmap& bitmap)
771 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
772 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800773 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700774{
775 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700776
halcanary385fe4d2015-08-26 13:07:48 -0700777 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700778 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779}
780
781SkCanvas::~SkCanvas() {
782 // free up the contents of our deque
783 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 this->internalRestore(); // restore the last, since we're going away
786
halcanary385fe4d2015-08-26 13:07:48 -0700787 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 dec_canvas();
790}
791
fmalita53d9f1c2016-01-25 06:23:54 -0800792#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793SkDrawFilter* SkCanvas::getDrawFilter() const {
794 return fMCRec->fFilter;
795}
796
797SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700798 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
800 return filter;
801}
fmalita77650002016-01-21 18:47:11 -0800802#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000804SkMetaData& SkCanvas::getMetaData() {
805 // metadata users are rare, so we lazily allocate it. If that changes we
806 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700807 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000808 fMetaData = new SkMetaData;
809 }
810 return *fMetaData;
811}
812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813///////////////////////////////////////////////////////////////////////////////
814
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000815void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700816 this->onFlush();
817}
818
819void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000820 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000821 if (device) {
822 device->flush();
823 }
824}
825
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000826SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000827 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000828 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
829}
830
senorblancoafc7cce2016-02-02 18:44:15 -0800831SkIRect SkCanvas::getTopLayerBounds() const {
832 SkBaseDevice* d = this->getTopDevice();
833 if (!d) {
834 return SkIRect::MakeEmpty();
835 }
836 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
837}
838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000839SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000841 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 SkASSERT(rec && rec->fLayer);
843 return rec->fLayer->fDevice;
844}
845
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000846SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000847 if (updateMatrixClip) {
848 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
849 }
reed@google.com9266fed2011-03-30 00:18:03 +0000850 return fMCRec->fTopLayer->fDevice;
851}
852
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000853bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700854 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000855 return false;
856 }
857
858 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700859 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700860 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000861 return false;
862 }
863 weAllocated = true;
864 }
865
reedcf01e312015-05-23 19:14:51 -0700866 SkAutoPixmapUnlock unlocker;
867 if (bitmap->requestLock(&unlocker)) {
868 const SkPixmap& pm = unlocker.pixmap();
869 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
870 return true;
871 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000872 }
873
874 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700875 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000876 }
877 return false;
878}
reed@google.com51df9e32010-12-23 19:29:18 +0000879
bsalomon@google.comc6980972011-11-02 19:57:21 +0000880bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000881 SkIRect r = srcRect;
882 const SkISize size = this->getBaseLayerSize();
883 if (!r.intersect(0, 0, size.width(), size.height())) {
884 bitmap->reset();
885 return false;
886 }
887
reed84825042014-09-02 12:50:45 -0700888 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000889 // bitmap will already be reset.
890 return false;
891 }
892 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
893 bitmap->reset();
894 return false;
895 }
896 return true;
897}
898
reed96472de2014-12-10 09:53:42 -0800899bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000900 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000901 if (!device) {
902 return false;
903 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000904 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800905
reed96472de2014-12-10 09:53:42 -0800906 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
907 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000908 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000909 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000910
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000911 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800912 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000913}
914
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000915bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700916 SkAutoPixmapUnlock unlocker;
917 if (bitmap.requestLock(&unlocker)) {
918 const SkPixmap& pm = unlocker.pixmap();
919 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000920 }
921 return false;
922}
923
924bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
925 int x, int y) {
926 switch (origInfo.colorType()) {
927 case kUnknown_SkColorType:
928 case kIndex_8_SkColorType:
929 return false;
930 default:
931 break;
932 }
halcanary96fcdcc2015-08-27 07:41:13 -0700933 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000934 return false;
935 }
936
937 const SkISize size = this->getBaseLayerSize();
938 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
939 if (!target.intersect(0, 0, size.width(), size.height())) {
940 return false;
941 }
942
943 SkBaseDevice* device = this->getDevice();
944 if (!device) {
945 return false;
946 }
947
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000948 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700949 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000950
951 // if x or y are negative, then we have to adjust pixels
952 if (x > 0) {
953 x = 0;
954 }
955 if (y > 0) {
956 y = 0;
957 }
958 // here x,y are either 0 or negative
959 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
960
reed4af35f32014-06-27 17:47:49 -0700961 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700962 const bool completeOverwrite = info.dimensions() == size;
963 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700964
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000965 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000966 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000967}
reed@google.com51df9e32010-12-23 19:29:18 +0000968
junov@google.com4370aed2012-01-18 16:21:08 +0000969SkCanvas* SkCanvas::canvasForDrawIter() {
970 return this;
971}
972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973//////////////////////////////////////////////////////////////////////////////
974
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975void SkCanvas::updateDeviceCMCache() {
976 if (fDeviceCMDirty) {
977 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700978 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000980
halcanary96fcdcc2015-08-27 07:41:13 -0700981 if (nullptr == layer->fNext) { // only one layer
982 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000984 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 do {
reed687fa1c2015-04-07 08:00:56 -0700986 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700987 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 }
989 fDeviceCMDirty = false;
990 }
991}
992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993///////////////////////////////////////////////////////////////////////////////
994
reed2ff1fce2014-12-11 07:07:37 -0800995void SkCanvas::checkForDeferredSave() {
996 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800997 this->doSave();
998 }
999}
1000
reedf0090cb2014-11-26 08:55:51 -08001001int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001002#ifdef SK_DEBUG
1003 int count = 0;
1004 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1005 for (;;) {
1006 const MCRec* rec = (const MCRec*)iter.next();
1007 if (!rec) {
1008 break;
1009 }
1010 count += 1 + rec->fDeferredSaveCount;
1011 }
1012 SkASSERT(count == fSaveCount);
1013#endif
1014 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001015}
1016
1017int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001018 fSaveCount += 1;
1019 fMCRec->fDeferredSaveCount += 1;
1020 return this->getSaveCount() - 1; // return our prev value
1021}
1022
1023void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001024 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001025
1026 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1027 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001028 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001029}
1030
1031void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001032 if (fMCRec->fDeferredSaveCount > 0) {
1033 SkASSERT(fSaveCount > 1);
1034 fSaveCount -= 1;
1035 fMCRec->fDeferredSaveCount -= 1;
1036 } else {
1037 // check for underflow
1038 if (fMCStack.count() > 1) {
1039 this->willRestore();
1040 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001041 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001042 this->internalRestore();
1043 this->didRestore();
1044 }
reedf0090cb2014-11-26 08:55:51 -08001045 }
1046}
1047
1048void SkCanvas::restoreToCount(int count) {
1049 // sanity check
1050 if (count < 1) {
1051 count = 1;
1052 }
mtkleinf0f14112014-12-12 08:46:25 -08001053
reedf0090cb2014-11-26 08:55:51 -08001054 int n = this->getSaveCount() - count;
1055 for (int i = 0; i < n; ++i) {
1056 this->restore();
1057 }
1058}
1059
reed2ff1fce2014-12-11 07:07:37 -08001060void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001062 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001064
reed687fa1c2015-04-07 08:00:56 -07001065 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066}
1067
reed4960eee2015-12-18 07:09:18 -08001068bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001069#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001070 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001071#else
1072 return true;
1073#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074}
1075
reed4960eee2015-12-18 07:09:18 -08001076bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001077 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001078 SkIRect clipBounds;
1079 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001080 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001081 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001082
reed96e657d2015-03-10 17:30:07 -07001083 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1084
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001085 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001086 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001087 if (bounds && !imageFilter->canComputeFastBounds()) {
1088 bounds = nullptr;
1089 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001090 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001091 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001092 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001094
reed96e657d2015-03-10 17:30:07 -07001095 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 r.roundOut(&ir);
1097 // early exit if the layer's bounds are clipped out
1098 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001099 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001100 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001101 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001102 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001103 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 }
1105 } else { // no user bounds, so just use the clip
1106 ir = clipBounds;
1107 }
reed180aec42015-03-11 10:39:04 -07001108 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109
reed4960eee2015-12-18 07:09:18 -08001110 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001111 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001112 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001113 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001114 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001115 }
1116
1117 if (intersection) {
1118 *intersection = ir;
1119 }
1120 return true;
1121}
1122
reed4960eee2015-12-18 07:09:18 -08001123
reed4960eee2015-12-18 07:09:18 -08001124int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1125 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001126}
1127
reed70ee31b2015-12-10 13:44:45 -08001128int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001129 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1130}
1131
1132int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1133 SaveLayerRec rec(origRec);
1134 if (gIgnoreSaveLayerBounds) {
1135 rec.fBounds = nullptr;
1136 }
1137 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1138 fSaveCount += 1;
1139 this->internalSaveLayer(rec, strategy);
1140 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001141}
1142
reeda2217ef2016-07-20 06:04:34 -07001143void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1144 SkBaseDevice* dst, const SkMatrix& ctm,
1145 const SkClipStack* clipStack) {
1146 SkDraw draw;
1147 SkRasterClip rc;
1148 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1149 if (!dst->accessPixels(&draw.fDst)) {
1150 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001151 }
reeda2217ef2016-07-20 06:04:34 -07001152 draw.fMatrix = &SkMatrix::I();
1153 draw.fRC = &rc;
1154 draw.fClipStack = clipStack;
1155 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001156
1157 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001158 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001159
1160 int x = src->getOrigin().x() - dst->getOrigin().x();
1161 int y = src->getOrigin().y() - dst->getOrigin().y();
1162 auto special = src->snapSpecial();
1163 if (special) {
1164 dst->drawSpecial(draw, special.get(), x, y, p);
1165 }
robertphillips7354a4b2015-12-16 05:08:27 -08001166}
reed70ee31b2015-12-10 13:44:45 -08001167
reed129ed1c2016-02-22 06:42:31 -08001168static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1169 const SkPaint* paint) {
1170 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1171 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001172 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001173 const bool hasImageFilter = paint && paint->getImageFilter();
1174
1175 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1176 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1177 // force to L32
1178 return SkImageInfo::MakeN32(w, h, alphaType);
1179 } else {
1180 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001181 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001182 }
1183}
1184
reed4960eee2015-12-18 07:09:18 -08001185void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1186 const SkRect* bounds = rec.fBounds;
1187 const SkPaint* paint = rec.fPaint;
1188 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1189
reed@google.comb93ba452014-03-10 19:47:58 +00001190#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001191 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001192#endif
1193
reed8c30a812016-04-20 16:36:51 -07001194 SkLazyPaint lazyP;
1195 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1196 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001197 SkMatrix remainder;
1198 SkSize scale;
1199 /*
1200 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1201 * but they do handle scaling. To accommodate this, we do the following:
1202 *
1203 * 1. Stash off the current CTM
1204 * 2. Decompose the CTM into SCALE and REMAINDER
1205 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1206 * contains the REMAINDER
1207 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1208 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1209 * of the original imagefilter, and draw that (via drawSprite)
1210 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1211 *
1212 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1213 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1214 */
reed96a04f32016-04-25 09:25:15 -07001215 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001216 stashedMatrix.decomposeScale(&scale, &remainder))
1217 {
1218 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1219 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1220 SkPaint* p = lazyP.set(*paint);
1221 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1222 SkFilterQuality::kLow_SkFilterQuality,
1223 sk_ref_sp(imageFilter)));
1224 imageFilter = p->getImageFilter();
1225 paint = p;
1226 }
reed8c30a812016-04-20 16:36:51 -07001227
junov@chromium.orga907ac32012-02-24 21:54:07 +00001228 // do this before we create the layer. We don't call the public save() since
1229 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001230 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001231
1232 fDeviceCMDirty = true;
1233
1234 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001235 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001236 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 }
1238
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001239 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1240 // the clipRectBounds() call above?
1241 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001242 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001243 }
1244
reed4960eee2015-12-18 07:09:18 -08001245 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001246 SkPixelGeometry geo = fProps.pixelGeometry();
1247 if (paint) {
reed76033be2015-03-14 10:54:31 -07001248 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001249 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001250 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001251 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001252 }
1253 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254
robertphillips5139e502016-07-19 05:10:40 -07001255 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001256 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001257 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001258 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001259 }
reedb2db8982014-11-13 12:41:02 -08001260
robertphillips5139e502016-07-19 05:10:40 -07001261 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001262 paint);
1263
robertphillips5139e502016-07-19 05:10:40 -07001264 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001265 {
reed70ee31b2015-12-10 13:44:45 -08001266 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001267 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001268 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001269 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001270 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001271 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1272 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001273 SkErrorInternals::SetError(kInternalError_SkError,
1274 "Unable to create device for layer.");
1275 return;
reed61f501f2015-04-29 08:34:00 -07001276 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001277 }
robertphillips5139e502016-07-19 05:10:40 -07001278 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001279
robertphillips5139e502016-07-19 05:10:40 -07001280 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281
1282 layer->fNext = fMCRec->fTopLayer;
1283 fMCRec->fLayer = layer;
1284 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001285
1286 if (rec.fBackdrop) {
1287 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1288 fMCRec->fMatrix, this->getClipStack());
1289 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290}
1291
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001292int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001293 if (0xFF == alpha) {
1294 return this->saveLayer(bounds, nullptr);
1295 } else {
1296 SkPaint tmpPaint;
1297 tmpPaint.setAlpha(alpha);
1298 return this->saveLayer(bounds, &tmpPaint);
1299 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001300}
1301
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302void SkCanvas::internalRestore() {
1303 SkASSERT(fMCStack.count() != 0);
1304
1305 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001306 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307
reed687fa1c2015-04-07 08:00:56 -07001308 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001309
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001310 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 DeviceCM* layer = fMCRec->fLayer; // may be null
1312 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001313 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314
1315 // now do the normal restore()
1316 fMCRec->~MCRec(); // balanced in save()
1317 fMCStack.pop_back();
1318 fMCRec = (MCRec*)fMCStack.back();
1319
1320 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1321 since if we're being recorded, we don't want to record this (the
1322 recorder will have already recorded the restore).
1323 */
bsalomon49f085d2014-09-05 13:34:00 -07001324 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001326 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001327 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001328 // restore what we smashed in internalSaveLayer
1329 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001330 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001332 delete layer;
reedb679ca82015-04-07 04:40:48 -07001333 } else {
1334 // we're at the root
reeda499f902015-05-01 09:34:31 -07001335 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001336 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001337 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001339 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340}
1341
reede8f30622016-03-23 18:59:25 -07001342sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001343 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001344 props = &fProps;
1345 }
1346 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001347}
1348
reede8f30622016-03-23 18:59:25 -07001349sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001350 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001351 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001352}
1353
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001354SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001355 return this->onImageInfo();
1356}
1357
1358SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001359 SkBaseDevice* dev = this->getDevice();
1360 if (dev) {
1361 return dev->imageInfo();
1362 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001363 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001364 }
1365}
1366
brianosman898235c2016-04-06 07:38:23 -07001367bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001368 return this->onGetProps(props);
1369}
1370
1371bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001372 SkBaseDevice* dev = this->getDevice();
1373 if (dev) {
1374 if (props) {
1375 *props = fProps;
1376 }
1377 return true;
1378 } else {
1379 return false;
1380 }
1381}
1382
reed6ceeebd2016-03-09 14:26:26 -08001383#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001384const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001385 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001386 if (this->peekPixels(&pmap)) {
1387 if (info) {
1388 *info = pmap.info();
1389 }
1390 if (rowBytes) {
1391 *rowBytes = pmap.rowBytes();
1392 }
1393 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001394 }
reed6ceeebd2016-03-09 14:26:26 -08001395 return nullptr;
1396}
1397#endif
1398
1399bool SkCanvas::peekPixels(SkPixmap* pmap) {
1400 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001401}
1402
reed884e97c2015-05-26 11:31:54 -07001403bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001404 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001405 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001406}
1407
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001408void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001409 SkPixmap pmap;
1410 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001411 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001412 }
1413 if (info) {
1414 *info = pmap.info();
1415 }
1416 if (rowBytes) {
1417 *rowBytes = pmap.rowBytes();
1418 }
1419 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001420 *origin = this->getTopDevice(false)->getOrigin();
1421 }
reed884e97c2015-05-26 11:31:54 -07001422 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001423}
1424
reed884e97c2015-05-26 11:31:54 -07001425bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001426 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001427 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001428}
1429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431
reed7503d602016-07-15 14:23:29 -07001432void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001434 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 paint = &tmp;
1436 }
reed@google.com4b226022011-01-11 18:32:13 +00001437
reed@google.com8926b162012-03-23 15:36:36 +00001438 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001439
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001441 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001442 paint = &looper.paint();
1443 SkImageFilter* filter = paint->getImageFilter();
1444 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001445 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001446 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001447 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001448 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 }
reeda2217ef2016-07-20 06:04:34 -07001451
reed@google.com4e2b3d32011-04-07 14:18:59 +00001452 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453}
1454
reed32704672015-12-16 08:27:10 -08001455/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001456
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001457void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001458 SkMatrix m;
1459 m.setTranslate(dx, dy);
1460 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001464 SkMatrix m;
1465 m.setScale(sx, sy);
1466 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467}
1468
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001469void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001470 SkMatrix m;
1471 m.setRotate(degrees);
1472 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473}
1474
bungeman7438bfc2016-07-12 15:01:19 -07001475void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1476 SkMatrix m;
1477 m.setRotate(degrees, px, py);
1478 this->concat(m);
1479}
1480
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001481void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001482 SkMatrix m;
1483 m.setSkew(sx, sy);
1484 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001485}
1486
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001487void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001488 if (matrix.isIdentity()) {
1489 return;
1490 }
1491
reed2ff1fce2014-12-11 07:07:37 -08001492 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001494 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001495 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001496
1497 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001498}
1499
reed8c30a812016-04-20 16:36:51 -07001500void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001502 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001503 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001504}
1505
1506void SkCanvas::setMatrix(const SkMatrix& matrix) {
1507 this->checkForDeferredSave();
1508 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001509 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510}
1511
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001513 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
vjiaoblack95302da2016-07-21 10:25:54 -07001516#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001517void SkCanvas::translateZ(SkScalar z) {
1518 this->checkForDeferredSave();
1519 this->fMCRec->fCurDrawDepth += z;
1520 this->didTranslateZ(z);
1521}
1522
1523SkScalar SkCanvas::getZ() const {
1524 return this->fMCRec->fCurDrawDepth;
1525}
1526
vjiaoblack95302da2016-07-21 10:25:54 -07001527void SkCanvas::setLights(sk_sp<SkLights> lights) {
1528 this->fLights = lights;
1529}
1530
1531sk_sp<SkLights> SkCanvas::getLights() const {
1532 return this->fLights;
1533}
1534#endif
1535
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536//////////////////////////////////////////////////////////////////////////////
1537
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001538void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001539 if (!fAllowSoftClip) {
1540 doAA = false;
1541 }
1542
1543#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1544 // Check if we can quick-accept the clip call (and do nothing)
1545 //
reed74467162016-06-30 08:15:35 -07001546 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
halcanaryc5769b22016-08-10 07:13:21 -07001547 SkRect devR;
1548 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001549 // NOTE: this check is CTM specific, since we might round differently with a different
1550 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1551 // applied later (i.e. if this is going into a picture).
1552 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1553#if 0
1554 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1555 rect.left(), rect.top(), rect.right(), rect.bottom());
1556#endif
1557 return;
1558 }
1559 }
1560#endif
1561
reed2ff1fce2014-12-11 07:07:37 -08001562 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1564 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565}
1566
1567void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001568#ifdef SK_ENABLE_CLIP_QUICKREJECT
1569 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001570 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001571 return;
reed@google.comda17f752012-08-16 18:27:05 +00001572 }
1573
reed@google.com3b3e8952012-08-16 20:53:31 +00001574 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001575 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001576 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001577
reed687fa1c2015-04-07 08:00:56 -07001578 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001579 (void)fMCRec->fRasterClip.setEmpty();
1580 return;
reed@google.comda17f752012-08-16 18:27:05 +00001581 }
1582 }
1583#endif
1584
reed74467162016-06-30 08:15:35 -07001585 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001586 SkRect devR;
reed74467162016-06-30 08:15:35 -07001587 if (isScaleTrans) {
halcanaryc5769b22016-08-10 07:13:21 -07001588 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001589 }
bsalomonac8cabd2015-11-20 18:53:07 -08001590
reed2d1afab2016-06-29 14:33:11 -07001591#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001592 if (SkRegion::kIntersect_Op == op &&
1593 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001594 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001595 {
1596 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1597#if 0
1598 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1599 rect.left(), rect.top(), rect.right(), rect.bottom());
1600#endif
1601 return;
1602 }
1603 }
reed2d1afab2016-06-29 14:33:11 -07001604#endif
reedc64eff52015-11-21 12:39:45 -08001605
1606 AutoValidateClip avc(this);
1607
1608 fDeviceCMDirty = true;
1609 fCachedLocalClipBoundsDirty = true;
1610
reed74467162016-06-30 08:15:35 -07001611 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001612 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1613 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001614 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001616 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001617 // and clip against that, since it can handle any matrix. However, to
1618 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1619 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 SkPath path;
1621
1622 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 }
1625}
1626
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001627void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001628 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001629 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001630 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1632 } else {
1633 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001634 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001636
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001638 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001639 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001640 AutoValidateClip avc(this);
1641
1642 fDeviceCMDirty = true;
1643 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001644 if (!fAllowSoftClip) {
1645 edgeStyle = kHard_ClipEdgeStyle;
1646 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001647
reed687fa1c2015-04-07 08:00:56 -07001648 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001649
senorblancoafc7cce2016-02-02 18:44:15 -08001650 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001651 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001652 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001653 }
1654
1655 SkPath path;
1656 path.addRRect(rrect);
1657 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001658 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001659}
1660
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001661void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001662 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001663 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001664
1665 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1666 SkRect r;
1667 if (path.isRect(&r)) {
1668 this->onClipRect(r, op, edgeStyle);
1669 return;
1670 }
1671 SkRRect rrect;
1672 if (path.isOval(&r)) {
1673 rrect.setOval(r);
1674 this->onClipRRect(rrect, op, edgeStyle);
1675 return;
1676 }
1677 if (path.isRRect(&rrect)) {
1678 this->onClipRRect(rrect, op, edgeStyle);
1679 return;
1680 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001681 }
robertphillips39f05382015-11-24 09:30:12 -08001682
1683 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001684}
1685
1686void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001687#ifdef SK_ENABLE_CLIP_QUICKREJECT
1688 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001689 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001690 return;
reed@google.comda17f752012-08-16 18:27:05 +00001691 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001692
reed@google.com3b3e8952012-08-16 20:53:31 +00001693 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001694 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001695 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001696
reed687fa1c2015-04-07 08:00:56 -07001697 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001698 (void)fMCRec->fRasterClip.setEmpty();
1699 return;
reed@google.comda17f752012-08-16 18:27:05 +00001700 }
1701 }
1702#endif
1703
reed@google.com5c3d1472011-02-22 19:12:23 +00001704 AutoValidateClip avc(this);
1705
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001707 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001708 if (!fAllowSoftClip) {
1709 edgeStyle = kHard_ClipEdgeStyle;
1710 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711
1712 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001713 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714
reed@google.comfe701122011-11-08 19:41:23 +00001715 // Check if the transfomation, or the original path itself
1716 // made us empty. Note this can also happen if we contained NaN
1717 // values. computing the bounds detects this, and will set our
1718 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1719 if (devPath.getBounds().isEmpty()) {
1720 // resetting the path will remove any NaN or other wanky values
1721 // that might upset our scan converter.
1722 devPath.reset();
1723 }
1724
reed@google.com5c3d1472011-02-22 19:12:23 +00001725 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001726 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001727
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001728 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001729 bool clipIsAA = getClipStack()->asPath(&devPath);
1730 if (clipIsAA) {
1731 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001732 }
fmalita1a481fe2015-02-04 07:39:34 -08001733
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001734 op = SkRegion::kReplace_Op;
1735 }
1736
senorblancoafc7cce2016-02-02 18:44:15 -08001737 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738}
1739
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001740void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001741 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001742 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001743}
1744
1745void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001746 AutoValidateClip avc(this);
1747
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001749 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750
reed@google.com5c3d1472011-02-22 19:12:23 +00001751 // todo: signal fClipStack that we have a region, and therefore (I guess)
1752 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001753 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001754
reed1f836ee2014-07-07 07:49:34 -07001755 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756}
1757
reed@google.com819c9212011-02-23 18:56:55 +00001758#ifdef SK_DEBUG
1759void SkCanvas::validateClip() const {
1760 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001761 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001762 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001763 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001764 return;
1765 }
1766
reed@google.com819c9212011-02-23 18:56:55 +00001767 SkIRect ir;
1768 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001769 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001770
reed687fa1c2015-04-07 08:00:56 -07001771 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001772 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001773 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001774 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001775 case SkClipStack::Element::kRect_Type:
1776 element->getRect().round(&ir);
1777 tmpClip.op(ir, element->getOp());
1778 break;
1779 case SkClipStack::Element::kEmpty_Type:
1780 tmpClip.setEmpty();
1781 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001782 default: {
1783 SkPath path;
1784 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001785 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001786 break;
1787 }
reed@google.com819c9212011-02-23 18:56:55 +00001788 }
1789 }
reed@google.com819c9212011-02-23 18:56:55 +00001790}
1791#endif
1792
reed@google.com90c07ea2012-04-13 13:50:27 +00001793void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001794 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001795 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001796
halcanary96fcdcc2015-08-27 07:41:13 -07001797 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001798 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001799 }
1800}
1801
reed@google.com5c3d1472011-02-22 19:12:23 +00001802///////////////////////////////////////////////////////////////////////////////
1803
reed@google.com754de5f2014-02-24 19:38:20 +00001804bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001805 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001806}
1807
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001808bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001809 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001810}
1811
reed@google.com3b3e8952012-08-16 20:53:31 +00001812bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001813 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001814 return true;
1815
reed1f836ee2014-07-07 07:49:34 -07001816 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 return true;
1818 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819
reed1f836ee2014-07-07 07:49:34 -07001820 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001821 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001822 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001823 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001824 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001825 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001826
reed@android.coma380ae42009-07-21 01:17:02 +00001827 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001828 // TODO: should we use | instead, or compare all 4 at once?
1829 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001830 return true;
1831 }
reed@google.comc0784db2013-12-13 21:16:12 +00001832 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001833 return true;
1834 }
1835 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837}
1838
reed@google.com3b3e8952012-08-16 20:53:31 +00001839bool SkCanvas::quickReject(const SkPath& path) const {
1840 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841}
1842
reed@google.com3b3e8952012-08-16 20:53:31 +00001843bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001844 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001845 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001846 return false;
1847 }
1848
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001849 SkMatrix inverse;
1850 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001851 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001852 if (bounds) {
1853 bounds->setEmpty();
1854 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001855 return false;
1856 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001857
bsalomon49f085d2014-09-05 13:34:00 -07001858 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001859 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001860 // adjust it outwards in case we are antialiasing
1861 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001862
reed@google.com8f4d2302013-12-17 16:44:46 +00001863 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1864 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865 inverse.mapRect(bounds, r);
1866 }
1867 return true;
1868}
1869
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001870bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001871 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001872 if (clip.isEmpty()) {
1873 if (bounds) {
1874 bounds->setEmpty();
1875 }
1876 return false;
1877 }
1878
bsalomon49f085d2014-09-05 13:34:00 -07001879 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001880 *bounds = clip.getBounds();
1881 }
1882 return true;
1883}
1884
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001886 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887}
1888
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001889const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001890 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001891}
1892
robertphillips175dd9b2016-04-28 14:32:04 -07001893GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001894 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001895 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001896}
1897
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001898GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001899 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001900 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001901}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001902
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001903void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1904 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001905 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001906 if (outer.isEmpty()) {
1907 return;
1908 }
1909 if (inner.isEmpty()) {
1910 this->drawRRect(outer, paint);
1911 return;
1912 }
1913
1914 // We don't have this method (yet), but technically this is what we should
1915 // be able to assert...
1916 // SkASSERT(outer.contains(inner));
1917 //
1918 // For now at least check for containment of bounds
1919 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1920
1921 this->onDrawDRRect(outer, inner, paint);
1922}
1923
reed41af9662015-01-05 07:49:08 -08001924// These need to stop being virtual -- clients need to override the onDraw... versions
1925
1926void SkCanvas::drawPaint(const SkPaint& paint) {
1927 this->onDrawPaint(paint);
1928}
1929
1930void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1931 this->onDrawRect(r, paint);
1932}
1933
1934void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1935 this->onDrawOval(r, paint);
1936}
1937
1938void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1939 this->onDrawRRect(rrect, paint);
1940}
1941
1942void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1943 this->onDrawPoints(mode, count, pts, paint);
1944}
1945
1946void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1947 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1948 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1949 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1950 indices, indexCount, paint);
1951}
1952
1953void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1954 this->onDrawPath(path, paint);
1955}
1956
reeda85d4d02015-05-06 12:56:48 -07001957void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001958 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001959 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001960}
1961
reede47829b2015-08-06 10:02:53 -07001962void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1963 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001964 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001965 if (dst.isEmpty() || src.isEmpty()) {
1966 return;
1967 }
1968 this->onDrawImageRect(image, &src, dst, paint, constraint);
1969}
reed41af9662015-01-05 07:49:08 -08001970
reed84984ef2015-07-17 07:09:43 -07001971void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1972 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001973 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001974 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001975}
1976
reede47829b2015-08-06 10:02:53 -07001977void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1978 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001979 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001980 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1981 constraint);
1982}
reede47829b2015-08-06 10:02:53 -07001983
reed4c21dc52015-06-25 12:32:03 -07001984void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1985 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001986 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001987 if (dst.isEmpty()) {
1988 return;
1989 }
msarett552bca92016-08-03 06:53:26 -07001990 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1991 this->onDrawImageNine(image, center, dst, paint);
1992 } else {
reede47829b2015-08-06 10:02:53 -07001993 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001994 }
reed4c21dc52015-06-25 12:32:03 -07001995}
1996
reed41af9662015-01-05 07:49:08 -08001997void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001998 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001999 return;
2000 }
reed41af9662015-01-05 07:49:08 -08002001 this->onDrawBitmap(bitmap, dx, dy, paint);
2002}
2003
reede47829b2015-08-06 10:02:53 -07002004void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002005 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002006 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002007 return;
2008 }
reede47829b2015-08-06 10:02:53 -07002009 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002010}
2011
reed84984ef2015-07-17 07:09:43 -07002012void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2013 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002014 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002015}
2016
reede47829b2015-08-06 10:02:53 -07002017void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2018 SrcRectConstraint constraint) {
2019 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2020 constraint);
2021}
reede47829b2015-08-06 10:02:53 -07002022
reed41af9662015-01-05 07:49:08 -08002023void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2024 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002025 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002026 return;
2027 }
msarett552bca92016-08-03 06:53:26 -07002028 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
2029 this->onDrawBitmapNine(bitmap, center, dst, paint);
2030 } else {
reeda5517e22015-07-14 10:54:12 -07002031 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002032 }
msarett552bca92016-08-03 06:53:26 -07002033
reed41af9662015-01-05 07:49:08 -08002034}
2035
msarettc573a402016-08-02 08:05:56 -07002036void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2037 const SkPaint* paint) {
2038 sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
2039 this->drawImageLattice(image.get(), lattice, dst, paint);
2040}
2041
2042void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2043 const SkPaint* paint) {
2044 RETURN_ON_NULL(image);
2045 if (dst.isEmpty()) {
2046 return;
2047 }
msarett552bca92016-08-03 06:53:26 -07002048 if (SkLatticeIter::Valid(image->width(), image->height(), lattice)) {
2049 this->onDrawImageLattice(image, lattice, dst, paint);
2050 } else {
msarettc573a402016-08-02 08:05:56 -07002051 this->drawImageRect(image, dst, paint);
2052 }
msarettc573a402016-08-02 08:05:56 -07002053}
2054
reed71c3c762015-06-24 10:29:17 -07002055void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2056 const SkColor colors[], int count, SkXfermode::Mode mode,
2057 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002058 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002059 if (count <= 0) {
2060 return;
2061 }
2062 SkASSERT(atlas);
2063 SkASSERT(xform);
2064 SkASSERT(tex);
2065 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2066}
2067
reedf70b5312016-03-04 16:36:20 -08002068void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2069 if (key) {
2070 this->onDrawAnnotation(rect, key, value);
2071 }
2072}
2073
reede47829b2015-08-06 10:02:53 -07002074void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2075 const SkPaint* paint, SrcRectConstraint constraint) {
2076 if (src) {
2077 this->drawImageRect(image, *src, dst, paint, constraint);
2078 } else {
2079 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2080 dst, paint, constraint);
2081 }
2082}
2083void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2084 const SkPaint* paint, SrcRectConstraint constraint) {
2085 if (src) {
2086 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2087 } else {
2088 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2089 dst, paint, constraint);
2090 }
2091}
2092
tomhudsoncb3bd182016-05-18 07:24:16 -07002093void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2094 SkIRect layer_bounds = this->getTopLayerBounds();
2095 if (matrix) {
2096 *matrix = this->getTotalMatrix();
2097 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2098 }
2099 if (clip_bounds) {
2100 this->getClipDeviceBounds(clip_bounds);
2101 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2102 }
2103}
2104
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105//////////////////////////////////////////////////////////////////////////////
2106// These are the virtual drawing methods
2107//////////////////////////////////////////////////////////////////////////////
2108
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002109void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002110 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002111 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2112 }
2113}
2114
reed41af9662015-01-05 07:49:08 -08002115void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002116 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002117 this->internalDrawPaint(paint);
2118}
2119
2120void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002121 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122
2123 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002124 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125 }
2126
reed@google.com4e2b3d32011-04-07 14:18:59 +00002127 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128}
2129
reed41af9662015-01-05 07:49:08 -08002130void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2131 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002132 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133 if ((long)count <= 0) {
2134 return;
2135 }
2136
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002137 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002138 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002139 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002140 // special-case 2 points (common for drawing a single line)
2141 if (2 == count) {
2142 r.set(pts[0], pts[1]);
2143 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002144 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002145 }
senorblanco87e066e2015-10-28 11:23:36 -07002146 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2147 return;
2148 }
2149 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002150 }
reed@google.coma584aed2012-05-16 14:06:02 +00002151
halcanary96fcdcc2015-08-27 07:41:13 -07002152 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002154 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002155
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002157 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158 }
reed@google.com4b226022011-01-11 18:32:13 +00002159
reed@google.com4e2b3d32011-04-07 14:18:59 +00002160 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161}
2162
reed41af9662015-01-05 07:49:08 -08002163void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002164 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002165 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002166 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002168 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2169 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2170 SkRect tmp(r);
2171 tmp.sort();
2172
senorblanco87e066e2015-10-28 11:23:36 -07002173 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2174 return;
2175 }
2176 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177 }
reed@google.com4b226022011-01-11 18:32:13 +00002178
reedc83a2972015-07-16 07:40:45 -07002179 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180
2181 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002182 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183 }
2184
reed@google.com4e2b3d32011-04-07 14:18:59 +00002185 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186}
2187
reed41af9662015-01-05 07:49:08 -08002188void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002189 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002190 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002191 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002192 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002193 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2194 return;
2195 }
2196 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002197 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002198
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002199 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002200
2201 while (iter.next()) {
2202 iter.fDevice->drawOval(iter, oval, looper.paint());
2203 }
2204
2205 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002206}
2207
reed41af9662015-01-05 07:49:08 -08002208void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002209 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002210 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002211 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002212 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002213 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2214 return;
2215 }
2216 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002217 }
2218
2219 if (rrect.isRect()) {
2220 // call the non-virtual version
2221 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002222 return;
2223 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002224 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002225 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2226 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002227 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002228
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002229 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002230
2231 while (iter.next()) {
2232 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2233 }
2234
2235 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002236}
2237
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002238void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2239 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002240 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002241 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002242 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002243 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2244 return;
2245 }
2246 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002247 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002248
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002249 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002250
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002251 while (iter.next()) {
2252 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2253 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002254
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002255 LOOPER_END
2256}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002257
reed41af9662015-01-05 07:49:08 -08002258void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002259 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002260 if (!path.isFinite()) {
2261 return;
2262 }
2263
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002264 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002265 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002266 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002267 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002268 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2269 return;
2270 }
2271 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002273
2274 const SkRect& r = path.getBounds();
2275 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002276 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002277 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002278 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002279 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002282 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283
2284 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002285 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 }
2287
reed@google.com4e2b3d32011-04-07 14:18:59 +00002288 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002289}
2290
reed262a71b2015-12-05 13:07:27 -08002291bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002292 if (!paint.getImageFilter()) {
2293 return false;
2294 }
2295
2296 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002297 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002298 return false;
2299 }
2300
2301 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2302 // Once we can filter and the filter will return a result larger than itself, we should be
2303 // able to remove this constraint.
2304 // skbug.com/4526
2305 //
2306 SkPoint pt;
2307 ctm.mapXY(x, y, &pt);
2308 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2309 return ir.contains(fMCRec->fRasterClip.getBounds());
2310}
2311
reeda85d4d02015-05-06 12:56:48 -07002312void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002313 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002314 SkRect bounds = SkRect::MakeXYWH(x, y,
2315 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002316 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002317 SkRect tmp = bounds;
2318 if (paint) {
2319 paint->computeFastBounds(tmp, &tmp);
2320 }
2321 if (this->quickReject(tmp)) {
2322 return;
2323 }
reeda85d4d02015-05-06 12:56:48 -07002324 }
halcanary9d524f22016-03-29 09:03:52 -07002325
reeda85d4d02015-05-06 12:56:48 -07002326 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002327 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002328 paint = lazy.init();
2329 }
reed262a71b2015-12-05 13:07:27 -08002330
reeda2217ef2016-07-20 06:04:34 -07002331 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002332 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2333 *paint);
2334 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002335 special = this->getDevice()->makeSpecial(image);
2336 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002337 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002338 }
2339 }
2340
reed262a71b2015-12-05 13:07:27 -08002341 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2342
reeda85d4d02015-05-06 12:56:48 -07002343 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002344 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002345 if (special) {
2346 SkPoint pt;
2347 iter.fMatrix->mapXY(x, y, &pt);
2348 iter.fDevice->drawSpecial(iter, special.get(),
2349 SkScalarRoundToInt(pt.fX),
2350 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002351 } else {
2352 iter.fDevice->drawImage(iter, image, x, y, pnt);
2353 }
reeda85d4d02015-05-06 12:56:48 -07002354 }
halcanary9d524f22016-03-29 09:03:52 -07002355
reeda85d4d02015-05-06 12:56:48 -07002356 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002357}
2358
msarettc573a402016-08-02 08:05:56 -07002359void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2360 const SkPaint* paint) {
2361 if (nullptr == paint || paint->canComputeFastBounds()) {
2362 SkRect storage;
2363 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2364 return;
2365 }
2366 }
2367
2368 SkLazyPaint lazy;
2369 if (nullptr == paint) {
2370 paint = lazy.init();
2371 }
2372
2373 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2374
2375 while (iter.next()) {
2376 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2377 }
2378
2379 LOOPER_END
2380}
2381
reed41af9662015-01-05 07:49:08 -08002382void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002383 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002384 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002385 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002386 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002387 if (paint) {
2388 paint->computeFastBounds(dst, &storage);
2389 }
2390 if (this->quickReject(storage)) {
2391 return;
2392 }
reeda85d4d02015-05-06 12:56:48 -07002393 }
2394 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002395 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002396 paint = lazy.init();
2397 }
halcanary9d524f22016-03-29 09:03:52 -07002398
senorblancoc41e7e12015-12-07 12:51:30 -08002399 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002400 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002401
reeda85d4d02015-05-06 12:56:48 -07002402 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002403 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002404 }
halcanary9d524f22016-03-29 09:03:52 -07002405
reeda85d4d02015-05-06 12:56:48 -07002406 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002407}
2408
reed41af9662015-01-05 07:49:08 -08002409void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002410 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411 SkDEBUGCODE(bitmap.validate();)
2412
reed33366972015-10-08 09:22:02 -07002413 if (bitmap.drawsNothing()) {
2414 return;
2415 }
2416
2417 SkLazyPaint lazy;
2418 if (nullptr == paint) {
2419 paint = lazy.init();
2420 }
2421
2422 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2423
2424 SkRect storage;
2425 const SkRect* bounds = nullptr;
2426 if (paint->canComputeFastBounds()) {
2427 bitmap.getBounds(&storage);
2428 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002429 SkRect tmp = storage;
2430 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2431 return;
2432 }
2433 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002434 }
reed@google.com4b226022011-01-11 18:32:13 +00002435
reeda2217ef2016-07-20 06:04:34 -07002436 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002437 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2438 *paint);
2439 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002440 special = this->getDevice()->makeSpecial(bitmap);
2441 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002442 drawAsSprite = false;
2443 }
2444 }
2445
reed262a71b2015-12-05 13:07:27 -08002446 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002447
2448 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002449 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002450 if (special) {
reed262a71b2015-12-05 13:07:27 -08002451 SkPoint pt;
2452 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002453 iter.fDevice->drawSpecial(iter, special.get(),
2454 SkScalarRoundToInt(pt.fX),
2455 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002456 } else {
2457 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2458 }
reed33366972015-10-08 09:22:02 -07002459 }
reeda2217ef2016-07-20 06:04:34 -07002460
reed33366972015-10-08 09:22:02 -07002461 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462}
2463
reed@google.com9987ec32011-09-07 11:57:52 +00002464// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002465void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002466 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002467 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002468 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002469 return;
2470 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002471
halcanary96fcdcc2015-08-27 07:41:13 -07002472 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002473 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002474 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2475 return;
2476 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002477 }
reed@google.com3d608122011-11-21 15:16:16 +00002478
reed@google.com33535f32012-09-25 15:37:50 +00002479 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002480 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002481 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002483
senorblancoc41e7e12015-12-07 12:51:30 -08002484 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002485 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002486
reed@google.com33535f32012-09-25 15:37:50 +00002487 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002488 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002489 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002490
reed@google.com33535f32012-09-25 15:37:50 +00002491 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002492}
2493
reed41af9662015-01-05 07:49:08 -08002494void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002495 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002496 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002497 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002498 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002499}
2500
reed4c21dc52015-06-25 12:32:03 -07002501void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2502 const SkPaint* paint) {
2503 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002504
halcanary96fcdcc2015-08-27 07:41:13 -07002505 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002506 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002507 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2508 return;
2509 }
reed@google.com3d608122011-11-21 15:16:16 +00002510 }
halcanary9d524f22016-03-29 09:03:52 -07002511
reed4c21dc52015-06-25 12:32:03 -07002512 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002513 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002514 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002515 }
halcanary9d524f22016-03-29 09:03:52 -07002516
senorblancoc41e7e12015-12-07 12:51:30 -08002517 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002518
reed4c21dc52015-06-25 12:32:03 -07002519 while (iter.next()) {
2520 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002521 }
halcanary9d524f22016-03-29 09:03:52 -07002522
reed4c21dc52015-06-25 12:32:03 -07002523 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002524}
2525
reed41af9662015-01-05 07:49:08 -08002526void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2527 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002528 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002529 SkDEBUGCODE(bitmap.validate();)
2530
halcanary96fcdcc2015-08-27 07:41:13 -07002531 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002532 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002533 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2534 return;
2535 }
reed4c21dc52015-06-25 12:32:03 -07002536 }
halcanary9d524f22016-03-29 09:03:52 -07002537
reed4c21dc52015-06-25 12:32:03 -07002538 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002539 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002540 paint = lazy.init();
2541 }
halcanary9d524f22016-03-29 09:03:52 -07002542
senorblancoc41e7e12015-12-07 12:51:30 -08002543 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002544
reed4c21dc52015-06-25 12:32:03 -07002545 while (iter.next()) {
2546 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2547 }
halcanary9d524f22016-03-29 09:03:52 -07002548
reed4c21dc52015-06-25 12:32:03 -07002549 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002550}
2551
reed@google.comf67e4cf2011-03-15 20:56:58 +00002552class SkDeviceFilteredPaint {
2553public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002554 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002555 uint32_t filteredFlags = device->filterTextFlags(paint);
2556 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002557 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002558 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002559 fPaint = newPaint;
2560 } else {
2561 fPaint = &paint;
2562 }
2563 }
2564
reed@google.comf67e4cf2011-03-15 20:56:58 +00002565 const SkPaint& paint() const { return *fPaint; }
2566
2567private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002568 const SkPaint* fPaint;
2569 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002570};
2571
bungeman@google.com52c748b2011-08-22 21:30:43 +00002572void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2573 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002574 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002575 draw.fDevice->drawRect(draw, r, paint);
2576 } else {
2577 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002578 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002579 draw.fDevice->drawRect(draw, r, p);
2580 }
2581}
2582
2583void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2584 const char text[], size_t byteLength,
2585 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002586 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002587
2588 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002589 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002590 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002591 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002592 return;
2593 }
2594
2595 SkScalar width = 0;
2596 SkPoint start;
2597
2598 start.set(0, 0); // to avoid warning
2599 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2600 SkPaint::kStrikeThruText_Flag)) {
2601 width = paint.measureText(text, byteLength);
2602
2603 SkScalar offsetX = 0;
2604 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2605 offsetX = SkScalarHalf(width);
2606 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2607 offsetX = width;
2608 }
2609 start.set(x - offsetX, y);
2610 }
2611
2612 if (0 == width) {
2613 return;
2614 }
2615
2616 uint32_t flags = paint.getFlags();
2617
2618 if (flags & (SkPaint::kUnderlineText_Flag |
2619 SkPaint::kStrikeThruText_Flag)) {
2620 SkScalar textSize = paint.getTextSize();
2621 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2622 SkRect r;
2623
2624 r.fLeft = start.fX;
2625 r.fRight = start.fX + width;
2626
2627 if (flags & SkPaint::kUnderlineText_Flag) {
2628 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2629 start.fY);
2630 r.fTop = offset;
2631 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002632 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002633 }
2634 if (flags & SkPaint::kStrikeThruText_Flag) {
2635 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2636 start.fY);
2637 r.fTop = offset;
2638 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002639 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002640 }
2641 }
2642}
2643
reed@google.come0d9ce82014-04-23 04:00:17 +00002644void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2645 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002646 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002647
2648 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002649 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002650 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002651 DrawTextDecorations(iter, dfp.paint(),
2652 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002653 }
2654
reed@google.com4e2b3d32011-04-07 14:18:59 +00002655 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002656}
2657
reed@google.come0d9ce82014-04-23 04:00:17 +00002658void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2659 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002660 SkPoint textOffset = SkPoint::Make(0, 0);
2661
halcanary96fcdcc2015-08-27 07:41:13 -07002662 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002663
reed@android.com8a1c16f2008-12-17 15:59:43 +00002664 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002665 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002666 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002667 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002668 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002669
reed@google.com4e2b3d32011-04-07 14:18:59 +00002670 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002671}
2672
reed@google.come0d9ce82014-04-23 04:00:17 +00002673void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2674 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002675
2676 SkPoint textOffset = SkPoint::Make(0, constY);
2677
halcanary96fcdcc2015-08-27 07:41:13 -07002678 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002679
reed@android.com8a1c16f2008-12-17 15:59:43 +00002680 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002681 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002682 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002683 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002684 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002685
reed@google.com4e2b3d32011-04-07 14:18:59 +00002686 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002687}
2688
reed@google.come0d9ce82014-04-23 04:00:17 +00002689void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2690 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002691 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002692
reed@android.com8a1c16f2008-12-17 15:59:43 +00002693 while (iter.next()) {
2694 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002695 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002696 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002697
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002698 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002699}
2700
reed45561a02016-07-07 12:47:17 -07002701void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2702 const SkRect* cullRect, const SkPaint& paint) {
2703 if (cullRect && this->quickReject(*cullRect)) {
2704 return;
2705 }
2706
2707 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2708
2709 while (iter.next()) {
2710 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2711 }
2712
2713 LOOPER_END
2714}
2715
fmalita00d5c2c2014-08-21 08:53:26 -07002716void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2717 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002718
fmalita85d5eb92015-03-04 11:20:12 -08002719 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002720 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002721 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002722 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002723 SkRect tmp;
2724 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2725 return;
2726 }
2727 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002728 }
2729
fmalita024f9962015-03-03 19:08:17 -08002730 // We cannot filter in the looper as we normally do, because the paint is
2731 // incomplete at this point (text-related attributes are embedded within blob run paints).
2732 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002733 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002734
fmalita85d5eb92015-03-04 11:20:12 -08002735 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002736
fmalitaaa1b9122014-08-28 14:32:24 -07002737 while (iter.next()) {
2738 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002739 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002740 }
2741
fmalitaaa1b9122014-08-28 14:32:24 -07002742 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002743
2744 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002745}
2746
reed@google.come0d9ce82014-04-23 04:00:17 +00002747// These will become non-virtual, so they always call the (virtual) onDraw... method
2748void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2749 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002750 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002751 if (byteLength) {
2752 this->onDrawText(text, byteLength, x, y, paint);
2753 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002754}
2755void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2756 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002757 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002758 if (byteLength) {
2759 this->onDrawPosText(text, byteLength, pos, paint);
2760 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002761}
2762void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2763 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002764 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002765 if (byteLength) {
2766 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2767 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002768}
2769void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2770 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002771 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002772 if (byteLength) {
2773 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2774 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002775}
reed45561a02016-07-07 12:47:17 -07002776void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2777 const SkRect* cullRect, const SkPaint& paint) {
2778 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2779 if (byteLength) {
2780 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2781 }
2782}
fmalita00d5c2c2014-08-21 08:53:26 -07002783void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2784 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002785 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002786 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002787 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002788}
reed@google.come0d9ce82014-04-23 04:00:17 +00002789
reed41af9662015-01-05 07:49:08 -08002790void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2791 const SkPoint verts[], const SkPoint texs[],
2792 const SkColor colors[], SkXfermode* xmode,
2793 const uint16_t indices[], int indexCount,
2794 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002795 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002796 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002797
reed@android.com8a1c16f2008-12-17 15:59:43 +00002798 while (iter.next()) {
2799 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002800 colors, xmode, indices, indexCount,
2801 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002802 }
reed@google.com4b226022011-01-11 18:32:13 +00002803
reed@google.com4e2b3d32011-04-07 14:18:59 +00002804 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002805}
2806
dandovb3c9d1c2014-08-12 08:34:29 -07002807void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2808 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002809 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002810 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002811 return;
2812 }
mtklein6cfa73a2014-08-13 13:33:49 -07002813
dandovecfff212014-08-04 10:02:00 -07002814 // Since a patch is always within the convex hull of the control points, we discard it when its
2815 // bounding rectangle is completely outside the current clip.
2816 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002817 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002818 if (this->quickReject(bounds)) {
2819 return;
2820 }
mtklein6cfa73a2014-08-13 13:33:49 -07002821
dandovb3c9d1c2014-08-12 08:34:29 -07002822 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2823}
2824
2825void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2826 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2827
halcanary96fcdcc2015-08-27 07:41:13 -07002828 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002829
dandovecfff212014-08-04 10:02:00 -07002830 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002831 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002832 }
mtklein6cfa73a2014-08-13 13:33:49 -07002833
dandovecfff212014-08-04 10:02:00 -07002834 LOOPER_END
2835}
2836
reeda8db7282015-07-07 10:22:31 -07002837void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002838 RETURN_ON_NULL(dr);
2839 if (x || y) {
2840 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2841 this->onDrawDrawable(dr, &matrix);
2842 } else {
2843 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002844 }
2845}
2846
reeda8db7282015-07-07 10:22:31 -07002847void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002848 RETURN_ON_NULL(dr);
2849 if (matrix && matrix->isIdentity()) {
2850 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002851 }
reede3b38ce2016-01-08 09:18:44 -08002852 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002853}
2854
2855void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2856 SkRect bounds = dr->getBounds();
2857 if (matrix) {
2858 matrix->mapRect(&bounds);
2859 }
2860 if (this->quickReject(bounds)) {
2861 return;
2862 }
2863 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002864}
2865
reed71c3c762015-06-24 10:29:17 -07002866void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2867 const SkColor colors[], int count, SkXfermode::Mode mode,
2868 const SkRect* cull, const SkPaint* paint) {
2869 if (cull && this->quickReject(*cull)) {
2870 return;
2871 }
2872
2873 SkPaint pnt;
2874 if (paint) {
2875 pnt = *paint;
2876 }
halcanary9d524f22016-03-29 09:03:52 -07002877
halcanary96fcdcc2015-08-27 07:41:13 -07002878 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002879 while (iter.next()) {
2880 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2881 }
2882 LOOPER_END
2883}
2884
reedf70b5312016-03-04 16:36:20 -08002885void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2886 SkASSERT(key);
2887
2888 SkPaint paint;
2889 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2890 while (iter.next()) {
2891 iter.fDevice->drawAnnotation(iter, rect, key, value);
2892 }
2893 LOOPER_END
2894}
2895
reed@android.com8a1c16f2008-12-17 15:59:43 +00002896//////////////////////////////////////////////////////////////////////////////
2897// These methods are NOT virtual, and therefore must call back into virtual
2898// methods, rather than actually drawing themselves.
2899//////////////////////////////////////////////////////////////////////////////
2900
2901void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002902 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002903 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002904 SkPaint paint;
2905
2906 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002907 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002908 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002909 }
2910 this->drawPaint(paint);
2911}
2912
reed@android.com845fdac2009-06-23 03:01:32 +00002913void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002914 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002915 SkPaint paint;
2916
2917 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002918 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002919 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920 }
2921 this->drawPaint(paint);
2922}
2923
2924void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002925 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002926 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002927
reed@android.com8a1c16f2008-12-17 15:59:43 +00002928 pt.set(x, y);
2929 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2930}
2931
2932void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002933 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934 SkPoint pt;
2935 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002936
reed@android.com8a1c16f2008-12-17 15:59:43 +00002937 pt.set(x, y);
2938 paint.setColor(color);
2939 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2940}
2941
2942void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2943 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002944 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002945 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002946
reed@android.com8a1c16f2008-12-17 15:59:43 +00002947 pts[0].set(x0, y0);
2948 pts[1].set(x1, y1);
2949 this->drawPoints(kLines_PointMode, 2, pts, paint);
2950}
2951
2952void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2953 SkScalar right, SkScalar bottom,
2954 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002955 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002956 SkRect r;
2957
2958 r.set(left, top, right, bottom);
2959 this->drawRect(r, paint);
2960}
2961
2962void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2963 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002964 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002965 if (radius < 0) {
2966 radius = 0;
2967 }
2968
2969 SkRect r;
2970 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002971 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002972}
2973
2974void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2975 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002976 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002978 SkRRect rrect;
2979 rrect.setRectXY(r, rx, ry);
2980 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002981 } else {
2982 this->drawRect(r, paint);
2983 }
2984}
2985
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2987 SkScalar sweepAngle, bool useCenter,
2988 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002989 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002990 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2991 this->drawOval(oval, paint);
2992 } else {
2993 SkPath path;
2994 if (useCenter) {
2995 path.moveTo(oval.centerX(), oval.centerY());
2996 }
2997 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2998 if (useCenter) {
2999 path.close();
3000 }
3001 this->drawPath(path, paint);
3002 }
3003}
3004
3005void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3006 const SkPath& path, SkScalar hOffset,
3007 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003008 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003010
reed@android.com8a1c16f2008-12-17 15:59:43 +00003011 matrix.setTranslate(hOffset, vOffset);
3012 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3013}
3014
reed@android.comf76bacf2009-05-13 14:00:33 +00003015///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003016
3017/**
3018 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3019 * against the playback cost of recursing into the subpicture to get at its actual ops.
3020 *
3021 * For now we pick a conservatively small value, though measurement (and other heuristics like
3022 * the type of ops contained) may justify changing this value.
3023 */
3024#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003025
reedd5fa1a42014-08-09 11:08:05 -07003026void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003027 RETURN_ON_NULL(picture);
3028
reed1c2c4412015-04-30 13:09:24 -07003029 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003030 if (matrix && matrix->isIdentity()) {
3031 matrix = nullptr;
3032 }
3033 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3034 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3035 picture->playback(this);
3036 } else {
3037 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003038 }
3039}
robertphillips9b14f262014-06-04 05:40:44 -07003040
reedd5fa1a42014-08-09 11:08:05 -07003041void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3042 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003043 if (!paint || paint->canComputeFastBounds()) {
3044 SkRect bounds = picture->cullRect();
3045 if (paint) {
3046 paint->computeFastBounds(bounds, &bounds);
3047 }
3048 if (matrix) {
3049 matrix->mapRect(&bounds);
3050 }
3051 if (this->quickReject(bounds)) {
3052 return;
3053 }
3054 }
3055
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003056 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003057 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003058}
3059
vjiaoblack95302da2016-07-21 10:25:54 -07003060#ifdef SK_EXPERIMENTAL_SHADOWING
3061void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3062 const SkMatrix* matrix,
3063 const SkPaint* paint) {
3064 RETURN_ON_NULL(picture);
3065
3066 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3067
3068 this->onDrawShadowedPicture(picture, matrix, paint);
3069}
3070
3071void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3072 const SkMatrix* matrix,
3073 const SkPaint* paint) {
vjiaoblack904527d2016-08-09 09:32:09 -07003074 if (!paint || paint->canComputeFastBounds()) {
3075 SkRect bounds = picture->cullRect();
3076 if (paint) {
3077 paint->computeFastBounds(bounds, &bounds);
3078 }
3079 if (matrix) {
3080 matrix->mapRect(&bounds);
3081 }
3082 if (this->quickReject(bounds)) {
3083 return;
3084 }
3085 }
3086
3087 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3088
3089 for (int i = 0; i < fLights->numLights(); ++i) {
3090 // skip over ambient lights; they don't cast shadows
3091 // lights that have shadow maps do not need updating (because lights are immutable)
3092
3093 if (SkLights::Light::kAmbient_LightType == fLights->light(i).type() ||
3094 fLights->light(i).getShadowMap() != nullptr) {
3095 continue;
3096 }
3097
3098 // TODO: compute the correct size of the depth map from the light properties
3099 // TODO: maybe add a kDepth_8_SkColorType
3100 // TODO: find actual max depth of picture
3101 SkISize shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3102 fLights->light(i), 255,
3103 picture->cullRect().width(),
3104 picture->cullRect().height());
3105
3106 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3107 kBGRA_8888_SkColorType,
3108 kOpaque_SkAlphaType);
3109
3110 // Create a new surface (that matches the backend of canvas)
3111 // for each shadow map
3112 sk_sp<SkSurface> surf(this->makeSurface(info));
3113
3114 // Wrap another SPFCanvas around the surface
3115 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3116 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3117
3118 // set the depth map canvas to have the light we're drawing.
3119 SkLights::Builder builder;
3120 builder.add(fLights->light(i));
3121 sk_sp<SkLights> curLight = builder.finish();
3122
3123 depthMapCanvas->setLights(std::move(curLight));
3124 depthMapCanvas->drawPicture(picture);
3125
3126 fLights->light(i).setShadowMap(surf->makeImageSnapshot());
3127 }
3128
3129 sk_sp<SkImage> povDepthMap;
3130 sk_sp<SkImage> diffuseMap;
3131
3132 // TODO: pass the depth to the shader in vertices, or uniforms
3133 // so we don't have to render depth and color separately
3134
3135 // povDepthMap
3136 {
3137 SkLights::Builder builder;
3138 builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3139 SkVector3::Make(0.0f, 0.0f, 1.0f)));
3140 sk_sp<SkLights> povLight = builder.finish();
3141
3142 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3143 picture->cullRect().height(),
3144 kBGRA_8888_SkColorType,
3145 kOpaque_SkAlphaType);
3146
3147 // Create a new surface (that matches the backend of canvas)
3148 // to create the povDepthMap
3149 sk_sp<SkSurface> surf(this->makeSurface(info));
3150
3151 // Wrap another SPFCanvas around the surface
3152 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3153 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3154
3155 // set the depth map canvas to have the light as the user's POV
3156 depthMapCanvas->setLights(std::move(povLight));
3157
3158 depthMapCanvas->drawPicture(picture);
3159
3160 povDepthMap = surf->makeImageSnapshot();
3161 }
3162
3163 // diffuseMap
3164 {
3165 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3166 picture->cullRect().height(),
3167 kBGRA_8888_SkColorType,
3168 kOpaque_SkAlphaType);
3169
3170 sk_sp<SkSurface> surf(this->makeSurface(info));
3171 surf->getCanvas()->drawPicture(picture);
3172
3173 diffuseMap = surf->makeImageSnapshot();
3174 }
3175
3176 SkPaint shadowPaint;
3177
3178 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3179 SkShader::kClamp_TileMode);
3180
3181 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3182 SkShader::kClamp_TileMode);
3183
3184 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3185 std::move(diffuseShader),
3186 std::move(fLights),
3187 diffuseMap->width(),
3188 diffuseMap->height());
3189
3190 shadowPaint.setShader(shadowShader);
3191
3192 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003193}
3194#endif
3195
reed@android.com8a1c16f2008-12-17 15:59:43 +00003196///////////////////////////////////////////////////////////////////////////////
3197///////////////////////////////////////////////////////////////////////////////
3198
3199SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003200 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003201
3202 SkASSERT(canvas);
3203
3204 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3205 fDone = !fImpl->next();
3206}
3207
3208SkCanvas::LayerIter::~LayerIter() {
3209 fImpl->~SkDrawIter();
3210}
3211
3212void SkCanvas::LayerIter::next() {
3213 fDone = !fImpl->next();
3214}
3215
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003216SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003217 return fImpl->getDevice();
3218}
3219
3220const SkMatrix& SkCanvas::LayerIter::matrix() const {
3221 return fImpl->getMatrix();
3222}
3223
3224const SkPaint& SkCanvas::LayerIter::paint() const {
3225 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003226 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003227 paint = &fDefaultPaint;
3228 }
3229 return *paint;
3230}
3231
reed1e7f5e72016-04-27 07:49:17 -07003232const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003233int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3234int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003235
3236///////////////////////////////////////////////////////////////////////////////
3237
fmalitac3b589a2014-06-05 12:40:07 -07003238SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003239
3240///////////////////////////////////////////////////////////////////////////////
3241
3242static bool supported_for_raster_canvas(const SkImageInfo& info) {
3243 switch (info.alphaType()) {
3244 case kPremul_SkAlphaType:
3245 case kOpaque_SkAlphaType:
3246 break;
3247 default:
3248 return false;
3249 }
3250
3251 switch (info.colorType()) {
3252 case kAlpha_8_SkColorType:
3253 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003254 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003255 break;
3256 default:
3257 return false;
3258 }
3259
3260 return true;
3261}
3262
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003263SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3264 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003265 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003266 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003267
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003268 SkBitmap bitmap;
3269 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003270 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003271 }
halcanary385fe4d2015-08-26 13:07:48 -07003272 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003273}
reedd5fa1a42014-08-09 11:08:05 -07003274
3275///////////////////////////////////////////////////////////////////////////////
3276
3277SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003278 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003279 : fCanvas(canvas)
3280 , fSaveCount(canvas->getSaveCount())
3281{
bsalomon49f085d2014-09-05 13:34:00 -07003282 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003283 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003284 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003285 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003286 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003287 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003288 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003289 canvas->save();
3290 }
mtklein6cfa73a2014-08-13 13:33:49 -07003291
bsalomon49f085d2014-09-05 13:34:00 -07003292 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003293 canvas->concat(*matrix);
3294 }
3295}
3296
3297SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3298 fCanvas->restoreToCount(fSaveCount);
3299}
reede8f30622016-03-23 18:59:25 -07003300
3301#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3302SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3303 return this->makeSurface(info, props).release();
3304}
3305#endif