blob: 7043ec3bc346396b7be596b1d0a865a47dc43f0a [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"
bungemand3ebb482015-08-05 13:57:49 -070013#include "SkDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080015#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkDrawFilter.h"
17#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080018#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070019#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000020#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070021#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070022#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070023#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000025#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080026#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000027#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000028#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000029#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070030#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000031#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000032#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080033#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070034
35#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000037#if SK_SUPPORT_GPU
38#include "GrRenderTarget.h"
39#endif
40
reedc83a2972015-07-16 07:40:45 -070041/*
42 * Return true if the drawing this rect would hit every pixels in the canvas.
43 *
44 * Returns false if
45 * - rect does not contain the canvas' bounds
46 * - paint is not fill
47 * - paint would blur or otherwise change the coverage of the rect
48 */
49bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
50 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070051 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
52 (int)kNone_ShaderOverrideOpacity,
53 "need_matching_enums0");
54 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
55 (int)kOpaque_ShaderOverrideOpacity,
56 "need_matching_enums1");
57 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
58 (int)kNotOpaque_ShaderOverrideOpacity,
59 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070060
61 const SkISize size = this->getBaseLayerSize();
62 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
63 if (!this->getClipStack()->quickContains(bounds)) {
64 return false;
65 }
66
67 if (rect) {
68 if (!this->getTotalMatrix().rectStaysRect()) {
69 return false; // conservative
70 }
71
72 SkRect devRect;
73 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070074 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070075 return false;
76 }
77 }
78
79 if (paint) {
80 SkPaint::Style paintStyle = paint->getStyle();
81 if (!(paintStyle == SkPaint::kFill_Style ||
82 paintStyle == SkPaint::kStrokeAndFill_Style)) {
83 return false;
84 }
85 if (paint->getMaskFilter() || paint->getLooper()
86 || paint->getPathEffect() || paint->getImageFilter()) {
87 return false; // conservative
88 }
89 }
90 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
91}
92
93///////////////////////////////////////////////////////////////////////////////////////////////////
94
reedd990e2f2014-12-22 11:58:30 -080095static bool gIgnoreSaveLayerBounds;
96void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
97 gIgnoreSaveLayerBounds = ignore;
98}
99bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
100 return gIgnoreSaveLayerBounds;
101}
102
reed0acf1b42014-12-22 16:12:38 -0800103static bool gTreatSpriteAsBitmap;
104void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
105 gTreatSpriteAsBitmap = spriteAsBitmap;
106}
107bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
108 return gTreatSpriteAsBitmap;
109}
110
reed@google.comda17f752012-08-16 18:27:05 +0000111// experimental for faster tiled drawing...
112//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +0000113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114//#define SK_TRACE_SAVERESTORE
115
116#ifdef SK_TRACE_SAVERESTORE
117 static int gLayerCounter;
118 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
119 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
120
121 static int gRecCounter;
122 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
123 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
124
125 static int gCanvasCounter;
126 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
127 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
128#else
129 #define inc_layer()
130 #define dec_layer()
131 #define inc_rec()
132 #define dec_rec()
133 #define inc_canvas()
134 #define dec_canvas()
135#endif
136
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000137typedef SkTLazy<SkPaint> SkLazyPaint;
138
reedc83a2972015-07-16 07:40:45 -0700139void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000140 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700141 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
142 ? SkSurface::kDiscard_ContentChangeMode
143 : SkSurface::kRetain_ContentChangeMode);
144 }
145}
146
147void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
148 ShaderOverrideOpacity overrideOpacity) {
149 if (fSurfaceBase) {
150 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
151 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
152 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
153 // and therefore we don't care which mode we're in.
154 //
155 if (fSurfaceBase->outstandingImageSnapshot()) {
156 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
157 mode = SkSurface::kDiscard_ContentChangeMode;
158 }
159 }
160 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000161 }
162}
163
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
reed4a8126e2014-09-22 07:29:03 -0700166static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
167 const uint32_t propFlags = props.flags();
168 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
169 flags &= ~SkPaint::kDither_Flag;
170 }
171 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
172 flags &= ~SkPaint::kAntiAlias_Flag;
173 }
174 return flags;
175}
176
177///////////////////////////////////////////////////////////////////////////////
178
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000179/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 The clip/matrix/proc are fields that reflect the top of the save/restore
181 stack. Whenever the canvas changes, it marks a dirty flag, and then before
182 these are used (assuming we're not on a layer) we rebuild these cache
183 values: they reflect the top of the save stack, but translated and clipped
184 by the device's XY offset and bitmap-bounds.
185*/
186struct DeviceCM {
187 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000188 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000189 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000190 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700191 const SkMatrix* fMatrix;
192 SkMatrix fMatrixStorage;
193 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
reed96e657d2015-03-10 17:30:07 -0700195 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700196 bool conservativeRasterClip, bool deviceIsBitmapDevice)
halcanary96fcdcc2015-08-27 07:41:13 -0700197 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700198 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700199 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700200 {
halcanary96fcdcc2015-08-27 07:41:13 -0700201 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000203 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 }
reed@google.com4b226022011-01-11 18:32:13 +0000205 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700206 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000209 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700210 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000211 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 fDevice->unref();
213 }
halcanary385fe4d2015-08-26 13:07:48 -0700214 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000215 }
reed@google.com4b226022011-01-11 18:32:13 +0000216
mtkleinfeaadee2015-04-08 11:25:48 -0700217 void reset(const SkIRect& bounds) {
218 SkASSERT(!fPaint);
219 SkASSERT(!fNext);
220 SkASSERT(fDevice);
221 fClip.setRect(bounds);
222 }
223
reed@google.com045e62d2011-10-24 12:19:46 +0000224 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
225 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000226 int x = fDevice->getOrigin().x();
227 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 int width = fDevice->width();
229 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000230
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if ((x | y) == 0) {
232 fMatrix = &totalMatrix;
233 fClip = totalClip;
234 } else {
235 fMatrixStorage = totalMatrix;
236 fMatrixStorage.postTranslate(SkIntToScalar(-x),
237 SkIntToScalar(-y));
238 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 totalClip.translate(-x, -y, &fClip);
241 }
242
reed@google.com045e62d2011-10-24 12:19:46 +0000243 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000248 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 SkRegion::kDifference_Op);
250 }
reed@google.com4b226022011-01-11 18:32:13 +0000251
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000252 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254#ifdef SK_DEBUG
255 if (!fClip.isEmpty()) {
256 SkIRect deviceR;
257 deviceR.set(0, 0, width, height);
258 SkASSERT(deviceR.contains(fClip.getBounds()));
259 }
260#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262};
263
264/* This is the record we keep for each save/restore level in the stack.
265 Since a level optionally copies the matrix and/or stack, we have pointers
266 for these fields. If the value is copied for this level, the copy is
267 stored in the ...Storage field, and the pointer points to that. If the
268 value is not copied for this level, we ignore ...Storage, and just point
269 at the corresponding value in the previous level in the stack.
270*/
271class SkCanvas::MCRec {
272public:
reed1f836ee2014-07-07 07:49:34 -0700273 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700274 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 /* If there are any layers in the stack, this points to the top-most
276 one that is at or below this level in the stack (so we know what
277 bitmap/device to draw into from this level. This value is NOT
278 reference counted, since the real owner is either our fLayer field,
279 or a previous one in a lower level.)
280 */
reed2ff1fce2014-12-11 07:07:37 -0800281 DeviceCM* fTopLayer;
282 SkRasterClip fRasterClip;
283 SkMatrix fMatrix;
284 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285
reedd9544982014-09-09 18:46:22 -0700286 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700287 fFilter = nullptr;
288 fLayer = nullptr;
289 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800290 fMatrix.reset();
291 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700292
reedd9544982014-09-09 18:46:22 -0700293 // don't bother initializing fNext
294 inc_rec();
295 }
reed2ff1fce2014-12-11 07:07:37 -0800296 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700297 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700298 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700299 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800300 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 // don't bother initializing fNext
303 inc_rec();
304 }
305 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000306 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700307 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 dec_rec();
309 }
mtkleinfeaadee2015-04-08 11:25:48 -0700310
311 void reset(const SkIRect& bounds) {
312 SkASSERT(fLayer);
313 SkASSERT(fDeferredSaveCount == 0);
314
315 fMatrix.reset();
316 fRasterClip.setRect(bounds);
317 fLayer->reset(bounds);
318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319};
320
321class SkDrawIter : public SkDraw {
322public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000323 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000324 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000325 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 canvas->updateDeviceCMCache();
327
reed687fa1c2015-04-07 08:00:56 -0700328 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000330 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 }
reed@google.com4b226022011-01-11 18:32:13 +0000332
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 bool next() {
334 // skip over recs with empty clips
335 if (fSkipEmptyClips) {
336 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
337 fCurrLayer = fCurrLayer->fNext;
338 }
339 }
340
reed@google.comf68c5e22012-02-24 16:38:58 +0000341 const DeviceCM* rec = fCurrLayer;
342 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343
344 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000345 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
346 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700348 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700349 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700350 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000352 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700355 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000356
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 return true;
358 }
359 return false;
360 }
reed@google.com4b226022011-01-11 18:32:13 +0000361
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000362 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000363 int getX() const { return fDevice->getOrigin().x(); }
364 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 const SkMatrix& getMatrix() const { return *fMatrix; }
366 const SkRegion& getClip() const { return *fClip; }
367 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369private:
370 SkCanvas* fCanvas;
371 const DeviceCM* fCurrLayer;
372 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 SkBool8 fSkipEmptyClips;
374
375 typedef SkDraw INHERITED;
376};
377
378/////////////////////////////////////////////////////////////////////////////
379
reeddbc3cef2015-04-29 12:18:57 -0700380static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
381 return lazy->isValid() ? lazy->get() : lazy->set(orig);
382}
383
384/**
385 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700386 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700387 */
388static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700389 SkImageFilter* imgf = paint.getImageFilter();
390 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700391 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700392 }
393
394 SkColorFilter* imgCF;
395 if (!imgf->asAColorFilter(&imgCF)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700396 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700397 }
398
399 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700400 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700401 // there is no existing paint colorfilter, so we can just return the imagefilter's
402 return imgCF;
403 }
404
405 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
406 // and we need to combine them into a single colorfilter.
407 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
408 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700409}
410
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411class AutoDrawLooper {
412public:
reed4a8126e2014-09-22 07:29:03 -0700413 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000414 bool skipLayerForImageFilter = false,
halcanary96fcdcc2015-08-27 07:41:13 -0700415 const SkRect* bounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700418 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700420 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422
reeddbc3cef2015-04-29 12:18:57 -0700423 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
424 if (simplifiedCF) {
425 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
426 paint->setColorFilter(simplifiedCF)->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700427 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700428 fPaint = paint;
429 }
430
431 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700432 /**
433 * We implement ImageFilters for a given draw by creating a layer, then applying the
434 * imagefilter to the pixels of that layer (its backing surface/image), and then
435 * we call restore() to xfer that layer to the main canvas.
436 *
437 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
438 * 2. Generate the src pixels:
439 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
440 * return (fPaint). We then draw the primitive (using srcover) into a cleared
441 * buffer/surface.
442 * 3. Restore the layer created in #1
443 * The imagefilter is passed the buffer/surface from the layer (now filled with the
444 * src pixels of the primitive). It returns a new "filtered" buffer, which we
445 * draw onto the previous layer using the xfermode from the original paint.
446 */
reed@google.com8926b162012-03-23 15:36:36 +0000447 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700448 tmp.setImageFilter(fPaint->getImageFilter());
449 tmp.setXfermode(fPaint->getXfermode());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000450 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700451 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700452 fTempLayerForImageFilter = true;
453 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000454 }
455
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000456 if (SkDrawLooper* looper = paint.getLooper()) {
457 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
458 looper->contextSize());
459 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000460 fIsSimple = false;
461 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700462 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000463 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700464 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000465 }
piotaixrb5fae932014-09-24 13:03:30 -0700466
reed4a8126e2014-09-22 07:29:03 -0700467 uint32_t oldFlags = paint.getFlags();
468 fNewPaintFlags = filter_paint_flags(props, oldFlags);
469 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700470 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700471 paint->setFlags(fNewPaintFlags);
472 fPaint = paint;
473 // if we're not simple, doNext() will take care of calling setFlags()
474 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000475 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700478 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000479 fCanvas->internalRestore();
480 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000483
reed@google.com4e2b3d32011-04-07 14:18:59 +0000484 const SkPaint& paint() const {
485 SkASSERT(fPaint);
486 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000488
reed@google.com129ec222012-05-15 13:24:09 +0000489 bool next(SkDrawFilter::Type drawType) {
490 if (fDone) {
491 return false;
492 } else if (fIsSimple) {
493 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000494 return !fPaint->nothingToDraw();
495 } else {
496 return this->doNext(drawType);
497 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000498 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500private:
reeddbc3cef2015-04-29 12:18:57 -0700501 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
502 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000503 SkCanvas* fCanvas;
504 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000505 SkDrawFilter* fFilter;
506 const SkPaint* fPaint;
507 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700508 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700509 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000510 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000511 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000512 SkDrawLooper::Context* fLooperContext;
513 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000514
515 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516};
517
reed@google.com129ec222012-05-15 13:24:09 +0000518bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700519 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000520 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700521 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000522
reeddbc3cef2015-04-29 12:18:57 -0700523 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
524 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700525 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000526
reed5c476fb2015-04-20 08:04:21 -0700527 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700528 paint->setImageFilter(nullptr);
529 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000530 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000531
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000532 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000533 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000534 return false;
535 }
536 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000537 if (!fFilter->filter(paint, drawType)) {
538 fDone = true;
539 return false;
540 }
halcanary96fcdcc2015-08-27 07:41:13 -0700541 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000542 // no looper means we only draw once
543 fDone = true;
544 }
545 }
546 fPaint = paint;
547
548 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000549 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000550 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000551 }
552
553 // call this after any possible paint modifiers
554 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700555 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000556 return false;
557 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000558 return true;
559}
560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561////////// macros to place around the internal draw calls //////////////////
562
reed@google.com8926b162012-03-23 15:36:36 +0000563#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000564 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700565 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000566 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000567 SkDrawIter iter(this);
568
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000569#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000570 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700571 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000574
reedc83a2972015-07-16 07:40:45 -0700575#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
576 this->predrawNotify(bounds, &paint, auxOpaque); \
577 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
578 while (looper.next(type)) { \
579 SkDrawIter iter(this);
580
reed@google.com4e2b3d32011-04-07 14:18:59 +0000581#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582
583////////////////////////////////////////////////////////////////////////////
584
mtkleinfeaadee2015-04-08 11:25:48 -0700585void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
586 this->restoreToCount(1);
587 fCachedLocalClipBounds.setEmpty();
588 fCachedLocalClipBoundsDirty = true;
589 fClipStack->reset();
590 fMCRec->reset(bounds);
591
592 // We're peering through a lot of structs here. Only at this scope do we
593 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
594 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
595}
596
reedd9544982014-09-09 18:46:22 -0700597SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
598 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000599 fCachedLocalClipBounds.setEmpty();
600 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000601 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000602 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700603 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800604 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700605 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606
halcanary385fe4d2015-08-26 13:07:48 -0700607 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700610 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
reeda499f902015-05-01 09:34:31 -0700612 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
613 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700614 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617
halcanary96fcdcc2015-08-27 07:41:13 -0700618 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000619
reedf92c8662014-08-18 08:02:43 -0700620 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700621 // The root device and the canvas should always have the same pixel geometry
622 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700623 if (device->forceConservativeRasterClip()) {
624 fConservativeRasterClip = true;
625 }
reedf92c8662014-08-18 08:02:43 -0700626 device->onAttachToCanvas(this);
627 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800628 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700629 }
630 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631}
632
reed@google.comcde92112011-07-06 20:00:52 +0000633SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000634 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700635 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000636{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000637 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000638
halcanary96fcdcc2015-08-27 07:41:13 -0700639 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000640}
641
reedd9544982014-09-09 18:46:22 -0700642static SkBitmap make_nopixels(int width, int height) {
643 SkBitmap bitmap;
644 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
645 return bitmap;
646}
647
648class SkNoPixelsBitmapDevice : public SkBitmapDevice {
649public:
robertphillipsfcf78292015-06-19 11:49:52 -0700650 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
651 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800652 {
653 this->setOrigin(bounds.x(), bounds.y());
654 }
reedd9544982014-09-09 18:46:22 -0700655
656private:
piotaixrb5fae932014-09-24 13:03:30 -0700657
reedd9544982014-09-09 18:46:22 -0700658 typedef SkBitmapDevice INHERITED;
659};
660
reed96a857e2015-01-25 10:33:58 -0800661SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000662 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800663 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000664{
665 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700666
halcanary385fe4d2015-08-26 13:07:48 -0700667 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
668 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700669}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000670
reed78e27682014-11-19 08:04:34 -0800671SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700672 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700673 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700674{
675 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700676
halcanary385fe4d2015-08-26 13:07:48 -0700677 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700678}
679
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000680SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000681 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700682 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000683{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700685
reedd9544982014-09-09 18:46:22 -0700686 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
robertphillipsfcf78292015-06-19 11:49:52 -0700689SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
690 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700691 , fProps(device->surfaceProps())
robertphillipsfcf78292015-06-19 11:49:52 -0700692{
693 inc_canvas();
694
695 this->init(device, flags);
696}
697
reed4a8126e2014-09-22 07:29:03 -0700698SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700699 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700700 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700701{
702 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700703
halcanary385fe4d2015-08-26 13:07:48 -0700704 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700705 this->init(device, kDefault_InitFlags);
706}
reed29c857d2014-09-21 10:25:07 -0700707
reed4a8126e2014-09-22 07:29:03 -0700708SkCanvas::SkCanvas(const SkBitmap& bitmap)
709 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
710 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
711{
712 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700713
halcanary385fe4d2015-08-26 13:07:48 -0700714 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700715 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716}
717
718SkCanvas::~SkCanvas() {
719 // free up the contents of our deque
720 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 this->internalRestore(); // restore the last, since we're going away
723
halcanary385fe4d2015-08-26 13:07:48 -0700724 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 dec_canvas();
727}
728
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729SkDrawFilter* SkCanvas::getDrawFilter() const {
730 return fMCRec->fFilter;
731}
732
733SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700734 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
736 return filter;
737}
738
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000739SkMetaData& SkCanvas::getMetaData() {
740 // metadata users are rare, so we lazily allocate it. If that changes we
741 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700742 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000743 fMetaData = new SkMetaData;
744 }
745 return *fMetaData;
746}
747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748///////////////////////////////////////////////////////////////////////////////
749
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000750void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000751 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000752 if (device) {
753 device->flush();
754 }
755}
756
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000757SkISize SkCanvas::getTopLayerSize() const {
758 SkBaseDevice* d = this->getTopDevice();
759 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
760}
761
762SkIPoint SkCanvas::getTopLayerOrigin() const {
763 SkBaseDevice* d = this->getTopDevice();
764 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
765}
766
767SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000768 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000769 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
770}
771
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000772SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000774 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 SkASSERT(rec && rec->fLayer);
776 return rec->fLayer->fDevice;
777}
778
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000779SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000780 if (updateMatrixClip) {
781 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
782 }
reed@google.com9266fed2011-03-30 00:18:03 +0000783 return fMCRec->fTopLayer->fDevice;
784}
785
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000786bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
787 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
788 return false;
789 }
790
791 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700792 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700793 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000794 return false;
795 }
796 weAllocated = true;
797 }
798
reedcf01e312015-05-23 19:14:51 -0700799 SkAutoPixmapUnlock unlocker;
800 if (bitmap->requestLock(&unlocker)) {
801 const SkPixmap& pm = unlocker.pixmap();
802 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
803 return true;
804 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000805 }
806
807 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700808 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000809 }
810 return false;
811}
reed@google.com51df9e32010-12-23 19:29:18 +0000812
bsalomon@google.comc6980972011-11-02 19:57:21 +0000813bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000814 SkIRect r = srcRect;
815 const SkISize size = this->getBaseLayerSize();
816 if (!r.intersect(0, 0, size.width(), size.height())) {
817 bitmap->reset();
818 return false;
819 }
820
reed84825042014-09-02 12:50:45 -0700821 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000822 // bitmap will already be reset.
823 return false;
824 }
825 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
826 bitmap->reset();
827 return false;
828 }
829 return true;
830}
831
reed96472de2014-12-10 09:53:42 -0800832bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000833 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000834 if (!device) {
835 return false;
836 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000837 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800838
reed96472de2014-12-10 09:53:42 -0800839 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
840 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000841 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000842 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000843
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000844 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800845 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000846}
847
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000848bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
849 if (bitmap.getTexture()) {
850 return false;
851 }
reedcf01e312015-05-23 19:14:51 -0700852
853 SkAutoPixmapUnlock unlocker;
854 if (bitmap.requestLock(&unlocker)) {
855 const SkPixmap& pm = unlocker.pixmap();
856 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000857 }
858 return false;
859}
860
861bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
862 int x, int y) {
863 switch (origInfo.colorType()) {
864 case kUnknown_SkColorType:
865 case kIndex_8_SkColorType:
866 return false;
867 default:
868 break;
869 }
halcanary96fcdcc2015-08-27 07:41:13 -0700870 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000871 return false;
872 }
873
874 const SkISize size = this->getBaseLayerSize();
875 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
876 if (!target.intersect(0, 0, size.width(), size.height())) {
877 return false;
878 }
879
880 SkBaseDevice* device = this->getDevice();
881 if (!device) {
882 return false;
883 }
884
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000885 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700886 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000887
888 // if x or y are negative, then we have to adjust pixels
889 if (x > 0) {
890 x = 0;
891 }
892 if (y > 0) {
893 y = 0;
894 }
895 // here x,y are either 0 or negative
896 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
897
reed4af35f32014-06-27 17:47:49 -0700898 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700899 const bool completeOverwrite = info.dimensions() == size;
900 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700901
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000902 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000903 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000904}
reed@google.com51df9e32010-12-23 19:29:18 +0000905
junov@google.com4370aed2012-01-18 16:21:08 +0000906SkCanvas* SkCanvas::canvasForDrawIter() {
907 return this;
908}
909
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910//////////////////////////////////////////////////////////////////////////////
911
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912void SkCanvas::updateDeviceCMCache() {
913 if (fDeviceCMDirty) {
914 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700915 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000917
halcanary96fcdcc2015-08-27 07:41:13 -0700918 if (nullptr == layer->fNext) { // only one layer
919 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000921 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 do {
reed687fa1c2015-04-07 08:00:56 -0700923 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700924 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 }
926 fDeviceCMDirty = false;
927 }
928}
929
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930///////////////////////////////////////////////////////////////////////////////
931
reed2ff1fce2014-12-11 07:07:37 -0800932void SkCanvas::checkForDeferredSave() {
933 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800934 this->doSave();
935 }
936}
937
reedf0090cb2014-11-26 08:55:51 -0800938int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800939#ifdef SK_DEBUG
940 int count = 0;
941 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
942 for (;;) {
943 const MCRec* rec = (const MCRec*)iter.next();
944 if (!rec) {
945 break;
946 }
947 count += 1 + rec->fDeferredSaveCount;
948 }
949 SkASSERT(count == fSaveCount);
950#endif
951 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800952}
953
954int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800955 fSaveCount += 1;
956 fMCRec->fDeferredSaveCount += 1;
957 return this->getSaveCount() - 1; // return our prev value
958}
959
960void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800961 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700962
963 SkASSERT(fMCRec->fDeferredSaveCount > 0);
964 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800965 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800966}
967
968void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800969 if (fMCRec->fDeferredSaveCount > 0) {
970 SkASSERT(fSaveCount > 1);
971 fSaveCount -= 1;
972 fMCRec->fDeferredSaveCount -= 1;
973 } else {
974 // check for underflow
975 if (fMCStack.count() > 1) {
976 this->willRestore();
977 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700978 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800979 this->internalRestore();
980 this->didRestore();
981 }
reedf0090cb2014-11-26 08:55:51 -0800982 }
983}
984
985void SkCanvas::restoreToCount(int count) {
986 // sanity check
987 if (count < 1) {
988 count = 1;
989 }
mtkleinf0f14112014-12-12 08:46:25 -0800990
reedf0090cb2014-11-26 08:55:51 -0800991 int n = this->getSaveCount() - count;
992 for (int i = 0; i < n; ++i) {
993 this->restore();
994 }
995}
996
reed2ff1fce2014-12-11 07:07:37 -0800997void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700999 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001001
reed687fa1c2015-04-07 08:00:56 -07001002 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003}
1004
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001006#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +00001008#else
1009 return true;
1010#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011}
1012
junov@chromium.orga907ac32012-02-24 21:54:07 +00001013bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -07001014 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001015 SkIRect clipBounds;
1016 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001017 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001018 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001019
reed96e657d2015-03-10 17:30:07 -07001020 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1021
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001022 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001023 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001024 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001025 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001026 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001028
reed96e657d2015-03-10 17:30:07 -07001029 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 r.roundOut(&ir);
1031 // early exit if the layer's bounds are clipped out
1032 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001033 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -07001034 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001035 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001036 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001037 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 }
1039 } else { // no user bounds, so just use the clip
1040 ir = clipBounds;
1041 }
reed180aec42015-03-11 10:39:04 -07001042 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +00001044 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -07001045 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001046 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001047 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001048 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001049 }
1050
1051 if (intersection) {
1052 *intersection = ir;
1053 }
1054 return true;
1055}
1056
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001057int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -08001058 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001059 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001060 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001061 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -07001062 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001063 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001064 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001065}
1066
reed2ff1fce2014-12-11 07:07:37 -08001067int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -08001068 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001069 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001070 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001071 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -07001072 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001073 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001074 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +00001075}
1076
reed2ff1fce2014-12-11 07:07:37 -08001077void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -07001078 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +00001079#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +00001080 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001081#endif
1082
junov@chromium.orga907ac32012-02-24 21:54:07 +00001083 // do this before we create the layer. We don't call the public save() since
1084 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001085 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001086
1087 fDeviceCMDirty = true;
1088
1089 SkIRect ir;
halcanary96fcdcc2015-08-27 07:41:13 -07001090 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001091 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 }
1093
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001094 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1095 // the clipRectBounds() call above?
1096 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001097 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001098 }
1099
reed76033be2015-03-14 10:54:31 -07001100 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001101 SkPixelGeometry geo = fProps.pixelGeometry();
1102 if (paint) {
reed76033be2015-03-14 10:54:31 -07001103 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001104 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001105 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001106 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001107 }
1108 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001109 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1110 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111
reedb2db8982014-11-13 12:41:02 -08001112 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001113 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001114 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001115 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001116 }
reedb2db8982014-11-13 12:41:02 -08001117
reed61f501f2015-04-29 08:34:00 -07001118 bool forceSpriteOnRestore = false;
1119 {
reeddaa57bf2015-05-15 10:39:17 -07001120 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed61f501f2015-04-29 08:34:00 -07001121 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo);
1122 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001123 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001124 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001125 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1126 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001127 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001128 SkErrorInternals::SetError(kInternalError_SkError,
1129 "Unable to create device for layer.");
1130 return;
1131 }
1132 forceSpriteOnRestore = true;
1133 }
1134 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001135 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001136
reed@google.com6f8f2922011-03-04 22:27:10 +00001137 device->setOrigin(ir.fLeft, ir.fTop);
halcanary385fe4d2015-08-26 13:07:48 -07001138 DeviceCM* layer =
1139 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 device->unref();
1141
1142 layer->fNext = fMCRec->fTopLayer;
1143 fMCRec->fLayer = layer;
1144 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145}
1146
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001147int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1148 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1149}
1150
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1152 SaveFlags flags) {
1153 if (0xFF == alpha) {
halcanary96fcdcc2015-08-27 07:41:13 -07001154 return this->saveLayer(bounds, nullptr, flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 } else {
1156 SkPaint tmpPaint;
1157 tmpPaint.setAlpha(alpha);
1158 return this->saveLayer(bounds, &tmpPaint, flags);
1159 }
1160}
1161
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162void SkCanvas::internalRestore() {
1163 SkASSERT(fMCStack.count() != 0);
1164
1165 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001166 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167
reed687fa1c2015-04-07 08:00:56 -07001168 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001169
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001170 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 DeviceCM* layer = fMCRec->fLayer; // may be null
1172 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001173 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174
1175 // now do the normal restore()
1176 fMCRec->~MCRec(); // balanced in save()
1177 fMCStack.pop_back();
1178 fMCRec = (MCRec*)fMCStack.back();
1179
1180 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1181 since if we're being recorded, we don't want to record this (the
1182 recorder will have already recorded the restore).
1183 */
bsalomon49f085d2014-09-05 13:34:00 -07001184 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001186 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001187 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001188 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001189 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001191 delete layer;
reedb679ca82015-04-07 04:40:48 -07001192 } else {
1193 // we're at the root
reeda499f902015-05-01 09:34:31 -07001194 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001195 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001197 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
reed4a8126e2014-09-22 07:29:03 -07001200SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001201 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001202 props = &fProps;
1203 }
1204 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001205}
1206
reed4a8126e2014-09-22 07:29:03 -07001207SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001208 SkBaseDevice* dev = this->getDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001209 return dev ? dev->newSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001210}
1211
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001212SkImageInfo SkCanvas::imageInfo() const {
1213 SkBaseDevice* dev = this->getDevice();
1214 if (dev) {
1215 return dev->imageInfo();
1216 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001217 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001218 }
1219}
1220
1221const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001222 SkPixmap pmap;
1223 if (!this->onPeekPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001224 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001225 }
1226 if (info) {
1227 *info = pmap.info();
1228 }
1229 if (rowBytes) {
1230 *rowBytes = pmap.rowBytes();
1231 }
1232 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001233}
1234
reed884e97c2015-05-26 11:31:54 -07001235bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001236 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001237 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001238}
1239
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001240void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001241 SkPixmap pmap;
1242 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001243 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001244 }
1245 if (info) {
1246 *info = pmap.info();
1247 }
1248 if (rowBytes) {
1249 *rowBytes = pmap.rowBytes();
1250 }
1251 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001252 *origin = this->getTopDevice(false)->getOrigin();
1253 }
reed884e97c2015-05-26 11:31:54 -07001254 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001255}
1256
reed884e97c2015-05-26 11:31:54 -07001257bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001258 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001259 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001260}
1261
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001262SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1263 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -07001264 if (nullptr == fAddr) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001265 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001266 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001267 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001268 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001269 if (!canvas->readPixels(&fBitmap, 0, 0)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001270 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001271 }
1272 fAddr = fBitmap.getPixels();
1273 fRowBytes = fBitmap.rowBytes();
1274 }
1275 SkASSERT(fAddr); // success
1276}
1277
1278bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1279 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001280 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001281 } else {
1282 bitmap->reset();
1283 return false;
1284 }
1285}
1286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001288void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001290 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 return;
1292 }
1293
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001294 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07001295 if (nullptr == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001296 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001298
1299 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001300
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001301 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07001302 const SkRect* bounds = nullptr;
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001303 if (paint && paint->canComputeFastBounds()) {
1304 bitmap.getBounds(&storage);
1305 matrix.mapRect(&storage);
1306 bounds = &paint->computeFastBounds(storage, &storage);
1307 }
1308
1309 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001310
1311 while (iter.next()) {
1312 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1313 }
1314
1315 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316}
1317
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001318void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001319 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001321 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 paint = &tmp;
1323 }
reed@google.com4b226022011-01-11 18:32:13 +00001324
reed@google.com8926b162012-03-23 15:36:36 +00001325 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001327 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001328 paint = &looper.paint();
1329 SkImageFilter* filter = paint->getImageFilter();
1330 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001331 if (filter && !dstDev->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001332 SkImageFilter::Proxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001333 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001334 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001335 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001336 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001337 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001338 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001339 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001340 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001341 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001342 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001343 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001344 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1345 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001346 }
reed61f501f2015-04-29 08:34:00 -07001347 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001348 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001349 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001350 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001351 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001354 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
reed41af9662015-01-05 07:49:08 -08001357void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001358 if (gTreatSpriteAsBitmap) {
1359 this->save();
1360 this->resetMatrix();
1361 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1362 this->restore();
1363 return;
1364 }
1365
danakj9881d632014-11-26 12:41:06 -08001366 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001367 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001368 return;
1369 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001370 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001371
reed@google.com8926b162012-03-23 15:36:36 +00001372 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001373 if (nullptr == paint) {
reed@google.com8926b162012-03-23 15:36:36 +00001374 paint = &tmp;
1375 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001376
reed@google.com8926b162012-03-23 15:36:36 +00001377 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001378
reed@google.com8926b162012-03-23 15:36:36 +00001379 while (iter.next()) {
1380 paint = &looper.paint();
1381 SkImageFilter* filter = paint->getImageFilter();
1382 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1383 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001384 SkImageFilter::Proxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001385 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001386 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001387 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001388 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001389 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001390 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001391 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001392 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001393 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001394 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001395 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001396 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001397 }
1398 } else {
1399 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1400 }
1401 }
1402 LOOPER_END
1403}
1404
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001406void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001407 SkMatrix m;
1408 m.setTranslate(dx, dy);
1409 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410}
1411
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001412void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001413 SkMatrix m;
1414 m.setScale(sx, sy);
1415 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416}
1417
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001418void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001419 SkMatrix m;
1420 m.setRotate(degrees);
1421 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422}
1423
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001424void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001425 SkMatrix m;
1426 m.setSkew(sx, sy);
1427 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001428}
1429
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001430void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001431 if (matrix.isIdentity()) {
1432 return;
1433 }
1434
reed2ff1fce2014-12-11 07:07:37 -08001435 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001437 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001438 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001439
1440 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001441}
1442
reed86a17e72015-05-14 12:25:22 -07001443void SkCanvas::setMatrix(const SkMatrix& matrix) {
1444 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001446 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001447 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001448 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451void SkCanvas::resetMatrix() {
1452 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001453
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 matrix.reset();
1455 this->setMatrix(matrix);
1456}
1457
1458//////////////////////////////////////////////////////////////////////////////
1459
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001460void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001461 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001462 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1463 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001464}
1465
1466void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001467#ifdef SK_ENABLE_CLIP_QUICKREJECT
1468 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001469 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001470 return false;
1471 }
1472
reed@google.com3b3e8952012-08-16 20:53:31 +00001473 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001474 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001475 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001476
reed687fa1c2015-04-07 08:00:56 -07001477 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001478 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001479 }
1480 }
1481#endif
1482
reed@google.com5c3d1472011-02-22 19:12:23 +00001483 AutoValidateClip avc(this);
1484
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001486 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487 if (!fAllowSoftClip) {
1488 edgeStyle = kHard_ClipEdgeStyle;
1489 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490
reed1f836ee2014-07-07 07:49:34 -07001491 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001492 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001493 // the matrix. This means we don't have to a) make a path, and b) tell
1494 // the region code to scan-convert the path, only to discover that it
1495 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
reed1f836ee2014-07-07 07:49:34 -07001498 fMCRec->fMatrix.mapRect(&r, rect);
reed687fa1c2015-04-07 08:00:56 -07001499 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001500 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001502 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001503 // and clip against that, since it can handle any matrix. However, to
1504 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1505 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 SkPath path;
1507
1508 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 }
1511}
1512
reed73e714e2014-09-04 09:02:23 -07001513static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1514 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001515 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001516}
1517
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001518void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001519 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001521 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001522 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1523 } else {
1524 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001525 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001527
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001529 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001530 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001531 AutoValidateClip avc(this);
1532
1533 fDeviceCMDirty = true;
1534 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001535 if (!fAllowSoftClip) {
1536 edgeStyle = kHard_ClipEdgeStyle;
1537 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001538
reed687fa1c2015-04-07 08:00:56 -07001539 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001540
1541 SkPath devPath;
1542 devPath.addRRect(transformedRRect);
1543
reed73e714e2014-09-04 09:02:23 -07001544 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001545 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001546 }
1547
1548 SkPath path;
1549 path.addRRect(rrect);
1550 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001552}
1553
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001554void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001555 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1557 SkRect r;
1558 if (!path.isInverseFillType() && path.isRect(&r)) {
1559 this->onClipRect(r, op, edgeStyle);
1560 } else {
1561 this->onClipPath(path, op, edgeStyle);
1562 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563}
1564
1565void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001566#ifdef SK_ENABLE_CLIP_QUICKREJECT
1567 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001568 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001569 return false;
1570 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001571
reed@google.com3b3e8952012-08-16 20:53:31 +00001572 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001573 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001574 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001575
reed687fa1c2015-04-07 08:00:56 -07001576 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001577 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001578 }
1579 }
1580#endif
1581
reed@google.com5c3d1472011-02-22 19:12:23 +00001582 AutoValidateClip avc(this);
1583
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001585 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001586 if (!fAllowSoftClip) {
1587 edgeStyle = kHard_ClipEdgeStyle;
1588 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589
1590 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001591 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592
reed@google.comfe701122011-11-08 19:41:23 +00001593 // Check if the transfomation, or the original path itself
1594 // made us empty. Note this can also happen if we contained NaN
1595 // values. computing the bounds detects this, and will set our
1596 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1597 if (devPath.getBounds().isEmpty()) {
1598 // resetting the path will remove any NaN or other wanky values
1599 // that might upset our scan converter.
1600 devPath.reset();
1601 }
1602
reed@google.com5c3d1472011-02-22 19:12:23 +00001603 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001604 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001605
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001606 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001607 bool clipIsAA = getClipStack()->asPath(&devPath);
1608 if (clipIsAA) {
1609 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001610 }
fmalita1a481fe2015-02-04 07:39:34 -08001611
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001612 op = SkRegion::kReplace_Op;
1613 }
1614
reed73e714e2014-09-04 09:02:23 -07001615 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616}
1617
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001618void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001619 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001620 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621}
1622
1623void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001624 AutoValidateClip avc(this);
1625
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001627 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628
reed@google.com5c3d1472011-02-22 19:12:23 +00001629 // todo: signal fClipStack that we have a region, and therefore (I guess)
1630 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001631 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001632
reed1f836ee2014-07-07 07:49:34 -07001633 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634}
1635
reed@google.com819c9212011-02-23 18:56:55 +00001636#ifdef SK_DEBUG
1637void SkCanvas::validateClip() const {
1638 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001639 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001640 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001641 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001642 return;
1643 }
1644
reed@google.com819c9212011-02-23 18:56:55 +00001645 SkIRect ir;
1646 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001647 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001648
reed687fa1c2015-04-07 08:00:56 -07001649 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001650 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001651 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001652 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001653 case SkClipStack::Element::kRect_Type:
1654 element->getRect().round(&ir);
1655 tmpClip.op(ir, element->getOp());
1656 break;
1657 case SkClipStack::Element::kEmpty_Type:
1658 tmpClip.setEmpty();
1659 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001660 default: {
1661 SkPath path;
1662 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001663 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001664 break;
1665 }
reed@google.com819c9212011-02-23 18:56:55 +00001666 }
1667 }
reed@google.com819c9212011-02-23 18:56:55 +00001668}
1669#endif
1670
reed@google.com90c07ea2012-04-13 13:50:27 +00001671void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001672 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001673 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001674
halcanary96fcdcc2015-08-27 07:41:13 -07001675 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001676 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001677 }
1678}
1679
reed@google.com5c3d1472011-02-22 19:12:23 +00001680///////////////////////////////////////////////////////////////////////////////
1681
reed@google.com754de5f2014-02-24 19:38:20 +00001682bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001683 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001684}
1685
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001686bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001687 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001688}
1689
reed@google.com3b3e8952012-08-16 20:53:31 +00001690bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001691 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001692 return true;
1693
reed1f836ee2014-07-07 07:49:34 -07001694 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 return true;
1696 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697
reed1f836ee2014-07-07 07:49:34 -07001698 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001699 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001700 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001701 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001702 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001703 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001704
reed@android.coma380ae42009-07-21 01:17:02 +00001705 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001706 // TODO: should we use | instead, or compare all 4 at once?
1707 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001708 return true;
1709 }
reed@google.comc0784db2013-12-13 21:16:12 +00001710 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001711 return true;
1712 }
1713 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715}
1716
reed@google.com3b3e8952012-08-16 20:53:31 +00001717bool SkCanvas::quickReject(const SkPath& path) const {
1718 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719}
1720
reed@google.com3b3e8952012-08-16 20:53:31 +00001721bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001722 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001723 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724 return false;
1725 }
1726
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001727 SkMatrix inverse;
1728 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001729 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001730 if (bounds) {
1731 bounds->setEmpty();
1732 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001733 return false;
1734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735
bsalomon49f085d2014-09-05 13:34:00 -07001736 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001737 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001738 // adjust it outwards in case we are antialiasing
1739 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001740
reed@google.com8f4d2302013-12-17 16:44:46 +00001741 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1742 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743 inverse.mapRect(bounds, r);
1744 }
1745 return true;
1746}
1747
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001748bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001749 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001750 if (clip.isEmpty()) {
1751 if (bounds) {
1752 bounds->setEmpty();
1753 }
1754 return false;
1755 }
1756
bsalomon49f085d2014-09-05 13:34:00 -07001757 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001758 *bounds = clip.getBounds();
1759 }
1760 return true;
1761}
1762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001764 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001767const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001768 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001769}
1770
reed@google.com9c135db2014-03-12 18:28:35 +00001771GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1772 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001773 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001774}
1775
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001776GrContext* SkCanvas::getGrContext() {
1777#if SK_SUPPORT_GPU
1778 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001779 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001780 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001781 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001782 return renderTarget->getContext();
1783 }
1784 }
1785#endif
1786
halcanary96fcdcc2015-08-27 07:41:13 -07001787 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001788
1789}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001790
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001791void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1792 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001793 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001794 if (outer.isEmpty()) {
1795 return;
1796 }
1797 if (inner.isEmpty()) {
1798 this->drawRRect(outer, paint);
1799 return;
1800 }
1801
1802 // We don't have this method (yet), but technically this is what we should
1803 // be able to assert...
1804 // SkASSERT(outer.contains(inner));
1805 //
1806 // For now at least check for containment of bounds
1807 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1808
1809 this->onDrawDRRect(outer, inner, paint);
1810}
1811
reed41af9662015-01-05 07:49:08 -08001812// These need to stop being virtual -- clients need to override the onDraw... versions
1813
1814void SkCanvas::drawPaint(const SkPaint& paint) {
1815 this->onDrawPaint(paint);
1816}
1817
1818void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1819 this->onDrawRect(r, paint);
1820}
1821
1822void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1823 this->onDrawOval(r, paint);
1824}
1825
1826void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1827 this->onDrawRRect(rrect, paint);
1828}
1829
1830void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1831 this->onDrawPoints(mode, count, pts, paint);
1832}
1833
1834void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1835 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1836 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1837 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1838 indices, indexCount, paint);
1839}
1840
1841void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1842 this->onDrawPath(path, paint);
1843}
1844
reeda85d4d02015-05-06 12:56:48 -07001845void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1846 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001847}
1848
reede47829b2015-08-06 10:02:53 -07001849void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1850 const SkPaint* paint, SrcRectConstraint constraint) {
1851 if (dst.isEmpty() || src.isEmpty()) {
1852 return;
1853 }
1854 this->onDrawImageRect(image, &src, dst, paint, constraint);
1855}
reed41af9662015-01-05 07:49:08 -08001856
reed84984ef2015-07-17 07:09:43 -07001857void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1858 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001859 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001860}
1861
reede47829b2015-08-06 10:02:53 -07001862void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1863 SrcRectConstraint constraint) {
1864 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1865 constraint);
1866}
reede47829b2015-08-06 10:02:53 -07001867
reed4c21dc52015-06-25 12:32:03 -07001868void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1869 const SkPaint* paint) {
1870 if (dst.isEmpty()) {
1871 return;
1872 }
1873 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001874 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001875 }
1876 this->onDrawImageNine(image, center, dst, paint);
1877}
1878
reed41af9662015-01-05 07:49:08 -08001879void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001880 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001881 return;
1882 }
reed41af9662015-01-05 07:49:08 -08001883 this->onDrawBitmap(bitmap, dx, dy, paint);
1884}
1885
reede47829b2015-08-06 10:02:53 -07001886void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001887 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001888 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001889 return;
1890 }
reede47829b2015-08-06 10:02:53 -07001891 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001892}
1893
reed84984ef2015-07-17 07:09:43 -07001894void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1895 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001896 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001897}
1898
reede47829b2015-08-06 10:02:53 -07001899void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1900 SrcRectConstraint constraint) {
1901 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1902 constraint);
1903}
reede47829b2015-08-06 10:02:53 -07001904
reed41af9662015-01-05 07:49:08 -08001905void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1906 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001907 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001908 return;
1909 }
reed4c21dc52015-06-25 12:32:03 -07001910 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001911 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001912 }
reed41af9662015-01-05 07:49:08 -08001913 this->onDrawBitmapNine(bitmap, center, dst, paint);
1914}
1915
1916void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001917 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001918 return;
1919 }
reed41af9662015-01-05 07:49:08 -08001920 this->onDrawSprite(bitmap, left, top, paint);
1921}
1922
reed71c3c762015-06-24 10:29:17 -07001923void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1924 const SkColor colors[], int count, SkXfermode::Mode mode,
1925 const SkRect* cull, const SkPaint* paint) {
1926 if (count <= 0) {
1927 return;
1928 }
1929 SkASSERT(atlas);
1930 SkASSERT(xform);
1931 SkASSERT(tex);
1932 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1933}
1934
reede47829b2015-08-06 10:02:53 -07001935void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1936 const SkPaint* paint, SrcRectConstraint constraint) {
1937 if (src) {
1938 this->drawImageRect(image, *src, dst, paint, constraint);
1939 } else {
1940 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
1941 dst, paint, constraint);
1942 }
1943}
1944void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1945 const SkPaint* paint, SrcRectConstraint constraint) {
1946 if (src) {
1947 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
1948 } else {
1949 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
1950 dst, paint, constraint);
1951 }
1952}
1953
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954//////////////////////////////////////////////////////////////////////////////
1955// These are the virtual drawing methods
1956//////////////////////////////////////////////////////////////////////////////
1957
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001958void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001959 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001960 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1961 }
1962}
1963
reed41af9662015-01-05 07:49:08 -08001964void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001965 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001966 this->internalDrawPaint(paint);
1967}
1968
1969void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001970 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971
1972 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001973 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974 }
1975
reed@google.com4e2b3d32011-04-07 14:18:59 +00001976 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977}
1978
reed41af9662015-01-05 07:49:08 -08001979void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1980 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001981 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982 if ((long)count <= 0) {
1983 return;
1984 }
1985
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001986 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07001987 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00001988 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001989 // special-case 2 points (common for drawing a single line)
1990 if (2 == count) {
1991 r.set(pts[0], pts[1]);
1992 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001993 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001994 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001995 bounds = &paint.computeFastStrokeBounds(r, &storage);
1996 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001997 return;
1998 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001999 }
reed@google.coma584aed2012-05-16 14:06:02 +00002000
halcanary96fcdcc2015-08-27 07:41:13 -07002001 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002003 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002004
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002006 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007 }
reed@google.com4b226022011-01-11 18:32:13 +00002008
reed@google.com4e2b3d32011-04-07 14:18:59 +00002009 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002010}
2011
reed41af9662015-01-05 07:49:08 -08002012void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002013 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002014 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002015 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002017 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2018 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2019 SkRect tmp(r);
2020 tmp.sort();
2021
2022 bounds = &paint.computeFastBounds(tmp, &storage);
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002023 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024 return;
2025 }
2026 }
reed@google.com4b226022011-01-11 18:32:13 +00002027
reedc83a2972015-07-16 07:40:45 -07002028 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002029
2030 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002031 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 }
2033
reed@google.com4e2b3d32011-04-07 14:18:59 +00002034 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035}
2036
reed41af9662015-01-05 07:49:08 -08002037void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002038 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002039 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002040 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002041 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002042 bounds = &paint.computeFastBounds(oval, &storage);
2043 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002044 return;
2045 }
2046 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002047
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002048 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002049
2050 while (iter.next()) {
2051 iter.fDevice->drawOval(iter, oval, looper.paint());
2052 }
2053
2054 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002055}
2056
reed41af9662015-01-05 07:49:08 -08002057void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002058 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002059 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002060 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002061 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002062 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2063 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002064 return;
2065 }
2066 }
2067
2068 if (rrect.isRect()) {
2069 // call the non-virtual version
2070 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002071 return;
2072 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002073 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002074 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2075 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002076 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002077
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002078 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002079
2080 while (iter.next()) {
2081 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2082 }
2083
2084 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002085}
2086
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002087void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2088 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002089 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002090 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002091 if (paint.canComputeFastBounds()) {
2092 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2093 if (this->quickReject(*bounds)) {
2094 return;
2095 }
2096 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002097
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002098 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002099
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002100 while (iter.next()) {
2101 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2102 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002103
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002104 LOOPER_END
2105}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002106
reed41af9662015-01-05 07:49:08 -08002107void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002108 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002109 if (!path.isFinite()) {
2110 return;
2111 }
2112
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002113 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002114 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002115 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002116 const SkRect& pathBounds = path.getBounds();
2117 bounds = &paint.computeFastBounds(pathBounds, &storage);
2118 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119 return;
2120 }
2121 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002122
2123 const SkRect& r = path.getBounds();
2124 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002125 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002126 this->internalDrawPaint(paint);
2127 }
2128 return;
2129 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002131 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132
2133 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002134 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135 }
2136
reed@google.com4e2b3d32011-04-07 14:18:59 +00002137 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002138}
2139
reeda85d4d02015-05-06 12:56:48 -07002140void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002141 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002142 SkRect bounds = SkRect::MakeXYWH(x, y,
2143 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002144 if (nullptr == paint || paint->canComputeFastBounds()) {
reeda85d4d02015-05-06 12:56:48 -07002145 if (paint) {
2146 paint->computeFastBounds(bounds, &bounds);
2147 }
2148 if (this->quickReject(bounds)) {
2149 return;
2150 }
2151 }
2152
2153 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002154 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002155 paint = lazy.init();
2156 }
2157
2158 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
2159
2160 while (iter.next()) {
2161 iter.fDevice->drawImage(iter, image, x, y, looper.paint());
2162 }
2163
2164 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002165}
2166
reed41af9662015-01-05 07:49:08 -08002167void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002168 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002169 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
reeda85d4d02015-05-06 12:56:48 -07002170 SkRect storage;
2171 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002172 if (nullptr == paint || paint->canComputeFastBounds()) {
reeda85d4d02015-05-06 12:56:48 -07002173 if (paint) {
2174 bounds = &paint->computeFastBounds(dst, &storage);
2175 }
2176 if (this->quickReject(*bounds)) {
2177 return;
2178 }
2179 }
2180 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002181 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002182 paint = lazy.init();
2183 }
2184
reedc83a2972015-07-16 07:40:45 -07002185 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2186 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002187
2188 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002189 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002190 }
2191
2192 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002193}
2194
reed41af9662015-01-05 07:49:08 -08002195void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002196 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 SkDEBUGCODE(bitmap.validate();)
2198
halcanary96fcdcc2015-08-27 07:41:13 -07002199 if (nullptr == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002200 SkRect bounds = {
2201 x, y,
2202 x + SkIntToScalar(bitmap.width()),
2203 y + SkIntToScalar(bitmap.height())
2204 };
2205 if (paint) {
2206 (void)paint->computeFastBounds(bounds, &bounds);
2207 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002208 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209 return;
2210 }
2211 }
reed@google.com4b226022011-01-11 18:32:13 +00002212
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213 SkMatrix matrix;
2214 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002215 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216}
2217
reed@google.com9987ec32011-09-07 11:57:52 +00002218// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002219void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002220 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002221 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002222 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002223 return;
2224 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002225
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002226 SkRect storage;
2227 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002228 if (nullptr == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002229 if (paint) {
2230 bounds = &paint->computeFastBounds(dst, &storage);
2231 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002232 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002233 return;
2234 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 }
reed@google.com3d608122011-11-21 15:16:16 +00002236
reed@google.com33535f32012-09-25 15:37:50 +00002237 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002238 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002239 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002241
reedc83a2972015-07-16 07:40:45 -07002242 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2243 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002244
reed@google.com33535f32012-09-25 15:37:50 +00002245 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002246 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002247 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002248
reed@google.com33535f32012-09-25 15:37:50 +00002249 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250}
2251
reed41af9662015-01-05 07:49:08 -08002252void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002253 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002254 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002255 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002256 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002257}
2258
reed4c21dc52015-06-25 12:32:03 -07002259void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2260 const SkPaint* paint) {
2261 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2262
2263 SkRect storage;
2264 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002265 if (nullptr == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002266 if (paint) {
2267 bounds = &paint->computeFastBounds(dst, &storage);
2268 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002269 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002270 return;
2271 }
2272 }
reed4c21dc52015-06-25 12:32:03 -07002273
2274 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002275 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002276 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002277 }
reed4c21dc52015-06-25 12:32:03 -07002278
2279 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2280
2281 while (iter.next()) {
2282 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002283 }
reed4c21dc52015-06-25 12:32:03 -07002284
2285 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002286}
2287
reed41af9662015-01-05 07:49:08 -08002288void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2289 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002290 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002291 SkDEBUGCODE(bitmap.validate();)
2292
reed4c21dc52015-06-25 12:32:03 -07002293 SkRect storage;
2294 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002295 if (nullptr == paint || paint->canComputeFastBounds()) {
reed4c21dc52015-06-25 12:32:03 -07002296 if (paint) {
2297 bounds = &paint->computeFastBounds(dst, &storage);
2298 }
2299 if (this->quickReject(*bounds)) {
2300 return;
2301 }
2302 }
2303
2304 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002305 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002306 paint = lazy.init();
2307 }
2308
2309 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2310
2311 while (iter.next()) {
2312 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2313 }
2314
2315 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002316}
2317
reed@google.comf67e4cf2011-03-15 20:56:58 +00002318class SkDeviceFilteredPaint {
2319public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002320 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002321 uint32_t filteredFlags = device->filterTextFlags(paint);
2322 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002323 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002324 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002325 fPaint = newPaint;
2326 } else {
2327 fPaint = &paint;
2328 }
2329 }
2330
reed@google.comf67e4cf2011-03-15 20:56:58 +00002331 const SkPaint& paint() const { return *fPaint; }
2332
2333private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002334 const SkPaint* fPaint;
2335 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002336};
2337
bungeman@google.com52c748b2011-08-22 21:30:43 +00002338void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2339 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002340 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002341 draw.fDevice->drawRect(draw, r, paint);
2342 } else {
2343 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002344 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002345 draw.fDevice->drawRect(draw, r, p);
2346 }
2347}
2348
2349void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2350 const char text[], size_t byteLength,
2351 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002352 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002353
2354 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002355 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002356 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002357 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002358 return;
2359 }
2360
2361 SkScalar width = 0;
2362 SkPoint start;
2363
2364 start.set(0, 0); // to avoid warning
2365 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2366 SkPaint::kStrikeThruText_Flag)) {
2367 width = paint.measureText(text, byteLength);
2368
2369 SkScalar offsetX = 0;
2370 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2371 offsetX = SkScalarHalf(width);
2372 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2373 offsetX = width;
2374 }
2375 start.set(x - offsetX, y);
2376 }
2377
2378 if (0 == width) {
2379 return;
2380 }
2381
2382 uint32_t flags = paint.getFlags();
2383
2384 if (flags & (SkPaint::kUnderlineText_Flag |
2385 SkPaint::kStrikeThruText_Flag)) {
2386 SkScalar textSize = paint.getTextSize();
2387 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2388 SkRect r;
2389
2390 r.fLeft = start.fX;
2391 r.fRight = start.fX + width;
2392
2393 if (flags & SkPaint::kUnderlineText_Flag) {
2394 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2395 start.fY);
2396 r.fTop = offset;
2397 r.fBottom = offset + height;
2398 DrawRect(draw, paint, r, textSize);
2399 }
2400 if (flags & SkPaint::kStrikeThruText_Flag) {
2401 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2402 start.fY);
2403 r.fTop = offset;
2404 r.fBottom = offset + height;
2405 DrawRect(draw, paint, r, textSize);
2406 }
2407 }
2408}
2409
reed@google.come0d9ce82014-04-23 04:00:17 +00002410void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2411 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002412 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413
2414 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002415 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002416 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002417 DrawTextDecorations(iter, dfp.paint(),
2418 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419 }
2420
reed@google.com4e2b3d32011-04-07 14:18:59 +00002421 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002422}
2423
reed@google.come0d9ce82014-04-23 04:00:17 +00002424void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2425 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002426 SkPoint textOffset = SkPoint::Make(0, 0);
2427
halcanary96fcdcc2015-08-27 07:41:13 -07002428 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002429
reed@android.com8a1c16f2008-12-17 15:59:43 +00002430 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002431 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002432 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002433 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002434 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002435
reed@google.com4e2b3d32011-04-07 14:18:59 +00002436 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437}
2438
reed@google.come0d9ce82014-04-23 04:00:17 +00002439void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2440 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002441
2442 SkPoint textOffset = SkPoint::Make(0, constY);
2443
halcanary96fcdcc2015-08-27 07:41:13 -07002444 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002445
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002447 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002448 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002449 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002451
reed@google.com4e2b3d32011-04-07 14:18:59 +00002452 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453}
2454
reed@google.come0d9ce82014-04-23 04:00:17 +00002455void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2456 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002457 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002458
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459 while (iter.next()) {
2460 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002461 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002463
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002464 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002465}
2466
fmalita00d5c2c2014-08-21 08:53:26 -07002467void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2468 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002469
fmalita85d5eb92015-03-04 11:20:12 -08002470 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002471 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002472 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002473 storage = blob->bounds().makeOffset(x, y);
2474 bounds = &paint.computeFastBounds(storage, &storage);
fmalita7ba7aa72014-08-29 09:46:36 -07002475
fmalita85d5eb92015-03-04 11:20:12 -08002476 if (this->quickReject(*bounds)) {
fmalita7ba7aa72014-08-29 09:46:36 -07002477 return;
2478 }
2479 }
2480
fmalita024f9962015-03-03 19:08:17 -08002481 // We cannot filter in the looper as we normally do, because the paint is
2482 // incomplete at this point (text-related attributes are embedded within blob run paints).
2483 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002484 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002485
fmalita85d5eb92015-03-04 11:20:12 -08002486 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002487
fmalitaaa1b9122014-08-28 14:32:24 -07002488 while (iter.next()) {
2489 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002490 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002491 }
2492
fmalitaaa1b9122014-08-28 14:32:24 -07002493 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002494
2495 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002496}
2497
reed@google.come0d9ce82014-04-23 04:00:17 +00002498// These will become non-virtual, so they always call the (virtual) onDraw... method
2499void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2500 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002501 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002502 this->onDrawText(text, byteLength, x, y, paint);
2503}
2504void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2505 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002506 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002507 this->onDrawPosText(text, byteLength, pos, paint);
2508}
2509void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2510 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002511 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002512 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2513}
2514void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2515 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002516 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002517 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2518}
fmalita00d5c2c2014-08-21 08:53:26 -07002519void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2520 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002521 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002522 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002523 this->onDrawTextBlob(blob, x, y, paint);
2524 }
2525}
reed@google.come0d9ce82014-04-23 04:00:17 +00002526
reed41af9662015-01-05 07:49:08 -08002527void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2528 const SkPoint verts[], const SkPoint texs[],
2529 const SkColor colors[], SkXfermode* xmode,
2530 const uint16_t indices[], int indexCount,
2531 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002532 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002533 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002534
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535 while (iter.next()) {
2536 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002537 colors, xmode, indices, indexCount,
2538 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002539 }
reed@google.com4b226022011-01-11 18:32:13 +00002540
reed@google.com4e2b3d32011-04-07 14:18:59 +00002541 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002542}
2543
dandovb3c9d1c2014-08-12 08:34:29 -07002544void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2545 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002546 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002547 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002548 return;
2549 }
mtklein6cfa73a2014-08-13 13:33:49 -07002550
dandovecfff212014-08-04 10:02:00 -07002551 // Since a patch is always within the convex hull of the control points, we discard it when its
2552 // bounding rectangle is completely outside the current clip.
2553 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002554 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002555 if (this->quickReject(bounds)) {
2556 return;
2557 }
mtklein6cfa73a2014-08-13 13:33:49 -07002558
dandovb3c9d1c2014-08-12 08:34:29 -07002559 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2560}
2561
2562void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2563 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2564
halcanary96fcdcc2015-08-27 07:41:13 -07002565 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002566
dandovecfff212014-08-04 10:02:00 -07002567 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002568 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002569 }
mtklein6cfa73a2014-08-13 13:33:49 -07002570
dandovecfff212014-08-04 10:02:00 -07002571 LOOPER_END
2572}
2573
reeda8db7282015-07-07 10:22:31 -07002574void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2575 if (dr) {
2576 if (x || y) {
2577 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2578 this->onDrawDrawable(dr, &matrix);
2579 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002580 this->onDrawDrawable(dr, nullptr);
reeda8db7282015-07-07 10:22:31 -07002581 }
reed6a070dc2014-11-11 19:36:09 -08002582 }
2583}
2584
reeda8db7282015-07-07 10:22:31 -07002585void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2586 if (dr) {
2587 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002588 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002589 }
2590 this->onDrawDrawable(dr, matrix);
2591 }
2592}
2593
2594void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2595 SkRect bounds = dr->getBounds();
2596 if (matrix) {
2597 matrix->mapRect(&bounds);
2598 }
2599 if (this->quickReject(bounds)) {
2600 return;
2601 }
2602 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002603}
2604
reed71c3c762015-06-24 10:29:17 -07002605void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2606 const SkColor colors[], int count, SkXfermode::Mode mode,
2607 const SkRect* cull, const SkPaint* paint) {
2608 if (cull && this->quickReject(*cull)) {
2609 return;
2610 }
2611
2612 SkPaint pnt;
2613 if (paint) {
2614 pnt = *paint;
2615 }
2616
halcanary96fcdcc2015-08-27 07:41:13 -07002617 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002618 while (iter.next()) {
2619 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2620 }
2621 LOOPER_END
2622}
2623
reed@android.com8a1c16f2008-12-17 15:59:43 +00002624//////////////////////////////////////////////////////////////////////////////
2625// These methods are NOT virtual, and therefore must call back into virtual
2626// methods, rather than actually drawing themselves.
2627//////////////////////////////////////////////////////////////////////////////
2628
2629void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002630 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002631 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002632 SkPaint paint;
2633
2634 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002635 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002636 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002637 }
2638 this->drawPaint(paint);
2639}
2640
reed@android.com845fdac2009-06-23 03:01:32 +00002641void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002642 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002643 SkPaint paint;
2644
2645 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002646 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002647 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002648 }
2649 this->drawPaint(paint);
2650}
2651
2652void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002653 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002654 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002655
reed@android.com8a1c16f2008-12-17 15:59:43 +00002656 pt.set(x, y);
2657 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2658}
2659
2660void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002661 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002662 SkPoint pt;
2663 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002664
reed@android.com8a1c16f2008-12-17 15:59:43 +00002665 pt.set(x, y);
2666 paint.setColor(color);
2667 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2668}
2669
2670void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2671 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002672 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002673 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002674
reed@android.com8a1c16f2008-12-17 15:59:43 +00002675 pts[0].set(x0, y0);
2676 pts[1].set(x1, y1);
2677 this->drawPoints(kLines_PointMode, 2, pts, paint);
2678}
2679
2680void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2681 SkScalar right, SkScalar bottom,
2682 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002683 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002684 SkRect r;
2685
2686 r.set(left, top, right, bottom);
2687 this->drawRect(r, paint);
2688}
2689
2690void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2691 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002692 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002693 if (radius < 0) {
2694 radius = 0;
2695 }
2696
2697 SkRect r;
2698 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002699 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002700}
2701
2702void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2703 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002704 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002705 if (rx > 0 && ry > 0) {
2706 if (paint.canComputeFastBounds()) {
2707 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002708 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002709 return;
2710 }
2711 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002712 SkRRect rrect;
2713 rrect.setRectXY(r, rx, ry);
2714 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002715 } else {
2716 this->drawRect(r, paint);
2717 }
2718}
2719
reed@android.com8a1c16f2008-12-17 15:59:43 +00002720void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2721 SkScalar sweepAngle, bool useCenter,
2722 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002723 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002724 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2725 this->drawOval(oval, paint);
2726 } else {
2727 SkPath path;
2728 if (useCenter) {
2729 path.moveTo(oval.centerX(), oval.centerY());
2730 }
2731 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2732 if (useCenter) {
2733 path.close();
2734 }
2735 this->drawPath(path, paint);
2736 }
2737}
2738
2739void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2740 const SkPath& path, SkScalar hOffset,
2741 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002742 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002743 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002744
reed@android.com8a1c16f2008-12-17 15:59:43 +00002745 matrix.setTranslate(hOffset, vOffset);
2746 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2747}
2748
reed@android.comf76bacf2009-05-13 14:00:33 +00002749///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002750
2751/**
2752 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2753 * against the playback cost of recursing into the subpicture to get at its actual ops.
2754 *
2755 * For now we pick a conservatively small value, though measurement (and other heuristics like
2756 * the type of ops contained) may justify changing this value.
2757 */
2758#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002759
reedd5fa1a42014-08-09 11:08:05 -07002760void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002761 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002762 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002763 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002764 matrix = nullptr;
reedd5fa1a42014-08-09 11:08:05 -07002765 }
reed1c2c4412015-04-30 13:09:24 -07002766 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2767 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2768 picture->playback(this);
2769 } else {
2770 this->onDrawPicture(picture, matrix, paint);
2771 }
reedd5fa1a42014-08-09 11:08:05 -07002772 }
2773}
robertphillips9b14f262014-06-04 05:40:44 -07002774
reedd5fa1a42014-08-09 11:08:05 -07002775void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2776 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002777 if (!paint || paint->canComputeFastBounds()) {
2778 SkRect bounds = picture->cullRect();
2779 if (paint) {
2780 paint->computeFastBounds(bounds, &bounds);
2781 }
2782 if (matrix) {
2783 matrix->mapRect(&bounds);
2784 }
2785 if (this->quickReject(bounds)) {
2786 return;
2787 }
2788 }
2789
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002790 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002791 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002792 // Canvas has to first give the device the opportunity to render
2793 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002794 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002795 return; // the device has rendered the entire picture
2796 }
2797 }
2798
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002799 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002800 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002801}
2802
reed@android.com8a1c16f2008-12-17 15:59:43 +00002803///////////////////////////////////////////////////////////////////////////////
2804///////////////////////////////////////////////////////////////////////////////
2805
2806SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002807 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002808
2809 SkASSERT(canvas);
2810
2811 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2812 fDone = !fImpl->next();
2813}
2814
2815SkCanvas::LayerIter::~LayerIter() {
2816 fImpl->~SkDrawIter();
2817}
2818
2819void SkCanvas::LayerIter::next() {
2820 fDone = !fImpl->next();
2821}
2822
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002823SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002824 return fImpl->getDevice();
2825}
2826
2827const SkMatrix& SkCanvas::LayerIter::matrix() const {
2828 return fImpl->getMatrix();
2829}
2830
2831const SkPaint& SkCanvas::LayerIter::paint() const {
2832 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002833 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002834 paint = &fDefaultPaint;
2835 }
2836 return *paint;
2837}
2838
2839const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2840int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2841int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002842
2843///////////////////////////////////////////////////////////////////////////////
2844
fmalitac3b589a2014-06-05 12:40:07 -07002845SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002846
2847///////////////////////////////////////////////////////////////////////////////
2848
2849static bool supported_for_raster_canvas(const SkImageInfo& info) {
2850 switch (info.alphaType()) {
2851 case kPremul_SkAlphaType:
2852 case kOpaque_SkAlphaType:
2853 break;
2854 default:
2855 return false;
2856 }
2857
2858 switch (info.colorType()) {
2859 case kAlpha_8_SkColorType:
2860 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002861 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002862 break;
2863 default:
2864 return false;
2865 }
2866
2867 return true;
2868}
2869
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002870SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2871 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002872 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002873 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002874
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002875 SkBitmap bitmap;
2876 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002877 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002878 }
halcanary385fe4d2015-08-26 13:07:48 -07002879 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002880}
reedd5fa1a42014-08-09 11:08:05 -07002881
2882///////////////////////////////////////////////////////////////////////////////
2883
2884SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002885 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002886 : fCanvas(canvas)
2887 , fSaveCount(canvas->getSaveCount())
2888{
bsalomon49f085d2014-09-05 13:34:00 -07002889 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002890 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002891 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002892 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002893 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002894 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002895 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002896 canvas->save();
2897 }
mtklein6cfa73a2014-08-13 13:33:49 -07002898
bsalomon49f085d2014-09-05 13:34:00 -07002899 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002900 canvas->concat(*matrix);
2901 }
2902}
2903
2904SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2905 fCanvas->restoreToCount(fSaveCount);
2906}