blob: 7949cb3be14cf2c901262a3df8fffdce52e05633 [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
8#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -07009#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000011#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080013#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080016#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070017#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000018#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000019#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070020#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000022#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080023#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000024#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000025#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000026#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070028#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000029#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000030#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080031#include "SkTraceEvent.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000032#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000033
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000034#if SK_SUPPORT_GPU
35#include "GrRenderTarget.h"
36#endif
37
reedd990e2f2014-12-22 11:58:30 -080038static bool gIgnoreSaveLayerBounds;
39void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
40 gIgnoreSaveLayerBounds = ignore;
41}
42bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
43 return gIgnoreSaveLayerBounds;
44}
45
reed0acf1b42014-12-22 16:12:38 -080046static bool gTreatSpriteAsBitmap;
47void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
48 gTreatSpriteAsBitmap = spriteAsBitmap;
49}
50bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
51 return gTreatSpriteAsBitmap;
52}
53
reed@google.comda17f752012-08-16 18:27:05 +000054// experimental for faster tiled drawing...
55//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000056
reed@android.com8a1c16f2008-12-17 15:59:43 +000057//#define SK_TRACE_SAVERESTORE
58
59#ifdef SK_TRACE_SAVERESTORE
60 static int gLayerCounter;
61 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
62 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
63
64 static int gRecCounter;
65 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
66 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
67
68 static int gCanvasCounter;
69 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
70 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
71#else
72 #define inc_layer()
73 #define dec_layer()
74 #define inc_rec()
75 #define dec_rec()
76 #define inc_canvas()
77 #define dec_canvas()
78#endif
79
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000080typedef SkTLazy<SkPaint> SkLazyPaint;
81
reed@google.com97af1a62012-08-28 12:19:02 +000082void SkCanvas::predrawNotify() {
83 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000084 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000085 }
86}
87
reed@android.com8a1c16f2008-12-17 15:59:43 +000088///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000089
reed4a8126e2014-09-22 07:29:03 -070090static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
91 const uint32_t propFlags = props.flags();
92 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
93 flags &= ~SkPaint::kDither_Flag;
94 }
95 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
96 flags &= ~SkPaint::kAntiAlias_Flag;
97 }
98 return flags;
99}
100
101///////////////////////////////////////////////////////////////////////////////
102
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000103/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 The clip/matrix/proc are fields that reflect the top of the save/restore
105 stack. Whenever the canvas changes, it marks a dirty flag, and then before
106 these are used (assuming we're not on a layer) we rebuild these cache
107 values: they reflect the top of the save stack, but translated and clipped
108 by the device's XY offset and bitmap-bounds.
109*/
110struct DeviceCM {
111 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000112 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000113 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000115 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116
reed96e657d2015-03-10 17:30:07 -0700117 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reedd9544982014-09-09 18:46:22 -0700118 bool conservativeRasterClip)
119 : fNext(NULL)
120 , fClip(conservativeRasterClip)
121 {
122 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000124 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 }
reed@google.com4b226022011-01-11 18:32:13 +0000126 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000128 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000130 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700131 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000132 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 fDevice->unref();
134 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000135 SkDELETE(fPaint);
136 }
reed@google.com4b226022011-01-11 18:32:13 +0000137
mtkleinfeaadee2015-04-08 11:25:48 -0700138 void reset(const SkIRect& bounds) {
139 SkASSERT(!fPaint);
140 SkASSERT(!fNext);
141 SkASSERT(fDevice);
142 fClip.setRect(bounds);
143 }
144
reed@google.com045e62d2011-10-24 12:19:46 +0000145 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
146 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000147 int x = fDevice->getOrigin().x();
148 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 int width = fDevice->width();
150 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 if ((x | y) == 0) {
153 fMatrix = &totalMatrix;
154 fClip = totalClip;
155 } else {
156 fMatrixStorage = totalMatrix;
157 fMatrixStorage.postTranslate(SkIntToScalar(-x),
158 SkIntToScalar(-y));
159 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 totalClip.translate(-x, -y, &fClip);
162 }
163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
166 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000169 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 SkRegion::kDifference_Op);
171 }
reed@google.com4b226022011-01-11 18:32:13 +0000172
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000173 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175#ifdef SK_DEBUG
176 if (!fClip.isEmpty()) {
177 SkIRect deviceR;
178 deviceR.set(0, 0, width, height);
179 SkASSERT(deviceR.contains(fClip.getBounds()));
180 }
181#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000182 }
183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000185 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186};
187
188/* This is the record we keep for each save/restore level in the stack.
189 Since a level optionally copies the matrix and/or stack, we have pointers
190 for these fields. If the value is copied for this level, the copy is
191 stored in the ...Storage field, and the pointer points to that. If the
192 value is not copied for this level, we ignore ...Storage, and just point
193 at the corresponding value in the previous level in the stack.
194*/
195class SkCanvas::MCRec {
196public:
reed1f836ee2014-07-07 07:49:34 -0700197 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700198 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 /* If there are any layers in the stack, this points to the top-most
200 one that is at or below this level in the stack (so we know what
201 bitmap/device to draw into from this level. This value is NOT
202 reference counted, since the real owner is either our fLayer field,
203 or a previous one in a lower level.)
204 */
reed2ff1fce2014-12-11 07:07:37 -0800205 DeviceCM* fTopLayer;
206 SkRasterClip fRasterClip;
207 SkMatrix fMatrix;
208 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209
reedd9544982014-09-09 18:46:22 -0700210 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
reedd9544982014-09-09 18:46:22 -0700211 fFilter = NULL;
212 fLayer = NULL;
213 fTopLayer = NULL;
reed2ff1fce2014-12-11 07:07:37 -0800214 fMatrix.reset();
215 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700216
reedd9544982014-09-09 18:46:22 -0700217 // don't bother initializing fNext
218 inc_rec();
219 }
reed2ff1fce2014-12-11 07:07:37 -0800220 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700221 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700223 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800224 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 // don't bother initializing fNext
227 inc_rec();
228 }
229 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000230 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 SkDELETE(fLayer);
232 dec_rec();
233 }
mtkleinfeaadee2015-04-08 11:25:48 -0700234
235 void reset(const SkIRect& bounds) {
236 SkASSERT(fLayer);
237 SkASSERT(fDeferredSaveCount == 0);
238
239 fMatrix.reset();
240 fRasterClip.setRect(bounds);
241 fLayer->reset(bounds);
242 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243};
244
245class SkDrawIter : public SkDraw {
246public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000247 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000248 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000249 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 canvas->updateDeviceCMCache();
251
reed687fa1c2015-04-07 08:00:56 -0700252 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000254 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 }
reed@google.com4b226022011-01-11 18:32:13 +0000256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 bool next() {
258 // skip over recs with empty clips
259 if (fSkipEmptyClips) {
260 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
261 fCurrLayer = fCurrLayer->fNext;
262 }
263 }
264
reed@google.comf68c5e22012-02-24 16:38:58 +0000265 const DeviceCM* rec = fCurrLayer;
266 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267
268 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000269 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
270 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 fDevice = rec->fDevice;
272 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000274 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275
276 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000278
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 return true;
280 }
281 return false;
282 }
reed@google.com4b226022011-01-11 18:32:13 +0000283
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000284 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000285 int getX() const { return fDevice->getOrigin().x(); }
286 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 const SkMatrix& getMatrix() const { return *fMatrix; }
288 const SkRegion& getClip() const { return *fClip; }
289 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000290
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291private:
292 SkCanvas* fCanvas;
293 const DeviceCM* fCurrLayer;
294 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 SkBool8 fSkipEmptyClips;
296
297 typedef SkDraw INHERITED;
298};
299
300/////////////////////////////////////////////////////////////////////////////
301
302class AutoDrawLooper {
303public:
reed4a8126e2014-09-22 07:29:03 -0700304 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000305 bool skipLayerForImageFilter = false,
306 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000307 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700309 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000310 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000311 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000312 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313
reed1b110d62015-03-08 18:47:13 -0700314 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
reed@google.com8926b162012-03-23 15:36:36 +0000315 SkPaint tmp;
reed1b110d62015-03-08 18:47:13 -0700316 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000317 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700318 SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000319 // we'll clear the imageFilter for the actual draws in next(), so
320 // it will only be applied during the restore().
321 fDoClearImageFilter = true;
322 }
323
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000324 if (SkDrawLooper* looper = paint.getLooper()) {
325 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
326 looper->contextSize());
327 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000328 fIsSimple = false;
329 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000330 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000331 // can we be marked as simple?
332 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000333 }
piotaixrb5fae932014-09-24 13:03:30 -0700334
reed4a8126e2014-09-22 07:29:03 -0700335 uint32_t oldFlags = paint.getFlags();
336 fNewPaintFlags = filter_paint_flags(props, oldFlags);
337 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reed1b110d62015-03-08 18:47:13 -0700338 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700339 paint->setFlags(fNewPaintFlags);
340 fPaint = paint;
341 // if we're not simple, doNext() will take care of calling setFlags()
342 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000343 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000344
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000346 if (fDoClearImageFilter) {
347 fCanvas->internalRestore();
348 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000349 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000351
reed@google.com4e2b3d32011-04-07 14:18:59 +0000352 const SkPaint& paint() const {
353 SkASSERT(fPaint);
354 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000356
reed@google.com129ec222012-05-15 13:24:09 +0000357 bool next(SkDrawFilter::Type drawType) {
358 if (fDone) {
359 return false;
360 } else if (fIsSimple) {
361 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000362 return !fPaint->nothingToDraw();
363 } else {
364 return this->doNext(drawType);
365 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000366 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000367
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368private:
reed1b110d62015-03-08 18:47:13 -0700369 SkLazyPaint fLazyPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000370 SkCanvas* fCanvas;
371 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000372 SkDrawFilter* fFilter;
373 const SkPaint* fPaint;
374 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700375 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000376 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000377 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000378 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000379 SkDrawLooper::Context* fLooperContext;
380 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000381
382 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383};
384
reed@google.com129ec222012-05-15 13:24:09 +0000385bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000386 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000387 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000388 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000389
reed1b110d62015-03-08 18:47:13 -0700390 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700391 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000392
393 if (fDoClearImageFilter) {
394 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000395 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000396
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000397 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000398 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000399 return false;
400 }
401 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000402 if (!fFilter->filter(paint, drawType)) {
403 fDone = true;
404 return false;
405 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000406 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000407 // no looper means we only draw once
408 fDone = true;
409 }
410 }
411 fPaint = paint;
412
413 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000414 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000415 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000416 }
417
418 // call this after any possible paint modifiers
419 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000420 fPaint = NULL;
421 return false;
422 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423 return true;
424}
425
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426////////// macros to place around the internal draw calls //////////////////
427
reed@google.com8926b162012-03-23 15:36:36 +0000428#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000429 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700430 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000431 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000432 SkDrawIter iter(this);
433
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000434#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000435 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700436 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000437 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000439
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441
442////////////////////////////////////////////////////////////////////////////
443
mtkleinfeaadee2015-04-08 11:25:48 -0700444void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
445 this->restoreToCount(1);
446 fCachedLocalClipBounds.setEmpty();
447 fCachedLocalClipBoundsDirty = true;
448 fClipStack->reset();
449 fMCRec->reset(bounds);
450
451 // We're peering through a lot of structs here. Only at this scope do we
452 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
453 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
454}
455
reedd9544982014-09-09 18:46:22 -0700456SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
457 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000458 fCachedLocalClipBounds.setEmpty();
459 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000460 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000461 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700462 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800463 fSaveCount = 1;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000464 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465
reed687fa1c2015-04-07 08:00:56 -0700466 fClipStack.reset(SkNEW(SkClipStack));
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700469 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470
reedb679ca82015-04-07 04:40:48 -0700471 SkASSERT(sizeof(DeviceCM) <= sizeof(fBaseLayerStorage));
472 fMCRec->fLayer = (DeviceCM*)fBaseLayerStorage;
473 new (fBaseLayerStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip);
474
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476
reed@google.com97af1a62012-08-28 12:19:02 +0000477 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000478
reedf92c8662014-08-18 08:02:43 -0700479 if (device) {
reedb2db8982014-11-13 12:41:02 -0800480 device->initForRootLayer(fProps.pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700481 if (device->forceConservativeRasterClip()) {
482 fConservativeRasterClip = true;
483 }
reedf92c8662014-08-18 08:02:43 -0700484 device->onAttachToCanvas(this);
485 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800486 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700487 }
488 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489}
490
reed@google.comcde92112011-07-06 20:00:52 +0000491SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000492 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700493 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000494{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000495 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000496
reedd9544982014-09-09 18:46:22 -0700497 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000498}
499
reedd9544982014-09-09 18:46:22 -0700500static SkBitmap make_nopixels(int width, int height) {
501 SkBitmap bitmap;
502 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
503 return bitmap;
504}
505
506class SkNoPixelsBitmapDevice : public SkBitmapDevice {
507public:
reed78e27682014-11-19 08:04:34 -0800508 SkNoPixelsBitmapDevice(const SkIRect& bounds)
509 : INHERITED(make_nopixels(bounds.width(), bounds.height()))
510 {
511 this->setOrigin(bounds.x(), bounds.y());
512 }
reedd9544982014-09-09 18:46:22 -0700513
514private:
piotaixrb5fae932014-09-24 13:03:30 -0700515
reedd9544982014-09-09 18:46:22 -0700516 typedef SkBitmapDevice INHERITED;
517};
518
reed96a857e2015-01-25 10:33:58 -0800519SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000520 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800521 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000522{
523 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700524
reed78e27682014-11-19 08:04:34 -0800525 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
526 (SkIRect::MakeWH(width, height))), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700527}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000528
reed78e27682014-11-19 08:04:34 -0800529SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700530 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700531 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700532{
533 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700534
reed78e27682014-11-19 08:04:34 -0800535 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700536}
537
reed4a8126e2014-09-22 07:29:03 -0700538SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700539 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700540 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700541{
542 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700543
reedd9544982014-09-09 18:46:22 -0700544 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000545}
546
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000547SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000548 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700549 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000550{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700552
reedd9544982014-09-09 18:46:22 -0700553 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554}
555
reed4a8126e2014-09-22 07:29:03 -0700556SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700557 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700558 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700559{
560 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700561
reed4a8126e2014-09-22 07:29:03 -0700562 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
563 this->init(device, kDefault_InitFlags);
564}
reed29c857d2014-09-21 10:25:07 -0700565
reed4a8126e2014-09-22 07:29:03 -0700566SkCanvas::SkCanvas(const SkBitmap& bitmap)
567 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
568 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
569{
570 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700571
reed4a8126e2014-09-22 07:29:03 -0700572 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
573 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574}
575
576SkCanvas::~SkCanvas() {
577 // free up the contents of our deque
578 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 this->internalRestore(); // restore the last, since we're going away
581
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000582 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000583
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 dec_canvas();
585}
586
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587SkDrawFilter* SkCanvas::getDrawFilter() const {
588 return fMCRec->fFilter;
589}
590
591SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
592 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
593 return filter;
594}
595
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000596SkMetaData& SkCanvas::getMetaData() {
597 // metadata users are rare, so we lazily allocate it. If that changes we
598 // can decide to just make it a field in the device (rather than a ptr)
599 if (NULL == fMetaData) {
600 fMetaData = new SkMetaData;
601 }
602 return *fMetaData;
603}
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605///////////////////////////////////////////////////////////////////////////////
606
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000607void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000608 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000609 if (device) {
610 device->flush();
611 }
612}
613
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000614SkISize SkCanvas::getTopLayerSize() const {
615 SkBaseDevice* d = this->getTopDevice();
616 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
617}
618
619SkIPoint SkCanvas::getTopLayerOrigin() const {
620 SkBaseDevice* d = this->getTopDevice();
621 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
622}
623
624SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000625 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000626 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
627}
628
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000629SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000631 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 SkASSERT(rec && rec->fLayer);
633 return rec->fLayer->fDevice;
634}
635
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000636SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000637 if (updateMatrixClip) {
638 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
639 }
reed@google.com9266fed2011-03-30 00:18:03 +0000640 return fMCRec->fTopLayer->fDevice;
641}
642
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000643bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
644 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
645 return false;
646 }
647
648 bool weAllocated = false;
649 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700650 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000651 return false;
652 }
653 weAllocated = true;
654 }
655
656 SkBitmap bm(*bitmap);
657 bm.lockPixels();
658 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
659 return true;
660 }
661
662 if (weAllocated) {
663 bitmap->setPixelRef(NULL);
664 }
665 return false;
666}
reed@google.com51df9e32010-12-23 19:29:18 +0000667
bsalomon@google.comc6980972011-11-02 19:57:21 +0000668bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000669 SkIRect r = srcRect;
670 const SkISize size = this->getBaseLayerSize();
671 if (!r.intersect(0, 0, size.width(), size.height())) {
672 bitmap->reset();
673 return false;
674 }
675
reed84825042014-09-02 12:50:45 -0700676 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000677 // bitmap will already be reset.
678 return false;
679 }
680 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
681 bitmap->reset();
682 return false;
683 }
684 return true;
685}
686
reed96472de2014-12-10 09:53:42 -0800687bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000688 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000689 if (!device) {
690 return false;
691 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000692 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800693
reed96472de2014-12-10 09:53:42 -0800694 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
695 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000696 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000697 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000698
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000699 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800700 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000701}
702
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000703bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
704 if (bitmap.getTexture()) {
705 return false;
706 }
707 SkBitmap bm(bitmap);
708 bm.lockPixels();
709 if (bm.getPixels()) {
710 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
711 }
712 return false;
713}
714
715bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
716 int x, int y) {
717 switch (origInfo.colorType()) {
718 case kUnknown_SkColorType:
719 case kIndex_8_SkColorType:
720 return false;
721 default:
722 break;
723 }
724 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
725 return false;
726 }
727
728 const SkISize size = this->getBaseLayerSize();
729 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
730 if (!target.intersect(0, 0, size.width(), size.height())) {
731 return false;
732 }
733
734 SkBaseDevice* device = this->getDevice();
735 if (!device) {
736 return false;
737 }
738
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000739 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700740 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000741
742 // if x or y are negative, then we have to adjust pixels
743 if (x > 0) {
744 x = 0;
745 }
746 if (y > 0) {
747 y = 0;
748 }
749 // here x,y are either 0 or negative
750 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
751
reed4af35f32014-06-27 17:47:49 -0700752 // Tell our owning surface to bump its generation ID
753 this->predrawNotify();
754
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000755 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000756 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000757}
reed@google.com51df9e32010-12-23 19:29:18 +0000758
junov@google.com4370aed2012-01-18 16:21:08 +0000759SkCanvas* SkCanvas::canvasForDrawIter() {
760 return this;
761}
762
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763//////////////////////////////////////////////////////////////////////////////
764
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765void SkCanvas::updateDeviceCMCache() {
766 if (fDeviceCMDirty) {
767 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700768 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000770
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 if (NULL == layer->fNext) { // only one layer
reed687fa1c2015-04-07 08:00:56 -0700772 layer->updateMC(totalMatrix, totalClip, *fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000774 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 do {
reed687fa1c2015-04-07 08:00:56 -0700776 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 } while ((layer = layer->fNext) != NULL);
778 }
779 fDeviceCMDirty = false;
780 }
781}
782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783///////////////////////////////////////////////////////////////////////////////
784
reed2ff1fce2014-12-11 07:07:37 -0800785void SkCanvas::checkForDeferredSave() {
786 if (fMCRec->fDeferredSaveCount > 0) {
787 fMCRec->fDeferredSaveCount -= 1;
788 this->doSave();
789 }
790}
791
reedf0090cb2014-11-26 08:55:51 -0800792int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800793#ifdef SK_DEBUG
794 int count = 0;
795 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
796 for (;;) {
797 const MCRec* rec = (const MCRec*)iter.next();
798 if (!rec) {
799 break;
800 }
801 count += 1 + rec->fDeferredSaveCount;
802 }
803 SkASSERT(count == fSaveCount);
804#endif
805 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800806}
807
808int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800809 fSaveCount += 1;
810 fMCRec->fDeferredSaveCount += 1;
811 return this->getSaveCount() - 1; // return our prev value
812}
813
814void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800815 this->willSave();
reed2ff1fce2014-12-11 07:07:37 -0800816 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800817}
818
819void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800820 if (fMCRec->fDeferredSaveCount > 0) {
821 SkASSERT(fSaveCount > 1);
822 fSaveCount -= 1;
823 fMCRec->fDeferredSaveCount -= 1;
824 } else {
825 // check for underflow
826 if (fMCStack.count() > 1) {
827 this->willRestore();
828 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700829 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800830 this->internalRestore();
831 this->didRestore();
832 }
reedf0090cb2014-11-26 08:55:51 -0800833 }
834}
835
836void SkCanvas::restoreToCount(int count) {
837 // sanity check
838 if (count < 1) {
839 count = 1;
840 }
mtkleinf0f14112014-12-12 08:46:25 -0800841
reedf0090cb2014-11-26 08:55:51 -0800842 int n = this->getSaveCount() - count;
843 for (int i = 0; i < n; ++i) {
844 this->restore();
845 }
846}
847
reed2ff1fce2014-12-11 07:07:37 -0800848void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700850 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000852
reed687fa1c2015-04-07 08:00:56 -0700853 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854}
855
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000857#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000859#else
860 return true;
861#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862}
863
junov@chromium.orga907ac32012-02-24 21:54:07 +0000864bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -0700865 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000866 SkIRect clipBounds;
867 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000868 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000869 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000870
reed96e657d2015-03-10 17:30:07 -0700871 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
872
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000873 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -0700874 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000875 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000876 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700877 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000879
reed96e657d2015-03-10 17:30:07 -0700880 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 r.roundOut(&ir);
882 // early exit if the layer's bounds are clipped out
883 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000884 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -0700885 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -0700886 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000887 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000888 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 }
890 } else { // no user bounds, so just use the clip
891 ir = clipBounds;
892 }
reed180aec42015-03-11 10:39:04 -0700893 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000895 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -0700896 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -0700897 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -0700898 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -0700899 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000900 }
901
902 if (intersection) {
903 *intersection = ir;
904 }
905 return true;
906}
907
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000908int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -0800909 if (gIgnoreSaveLayerBounds) {
910 bounds = NULL;
911 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000912 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -0700913 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -0700914 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -0800915 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000916}
917
reed2ff1fce2014-12-11 07:07:37 -0800918int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -0800919 if (gIgnoreSaveLayerBounds) {
920 bounds = NULL;
921 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000922 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -0700923 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -0700924 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -0800925 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +0000926}
927
reed2ff1fce2014-12-11 07:07:37 -0800928void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -0700929 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000930#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000931 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000932#endif
933
junov@chromium.orga907ac32012-02-24 21:54:07 +0000934 // do this before we create the layer. We don't call the public save() since
935 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -0800936 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000937
938 fDeviceCMDirty = true;
939
940 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000941 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -0800942 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 }
944
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000945 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
946 // the clipRectBounds() call above?
947 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -0800948 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000949 }
950
reed76033be2015-03-14 10:54:31 -0700951 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -0700952 SkPixelGeometry geo = fProps.pixelGeometry();
953 if (paint) {
reed76033be2015-03-14 10:54:31 -0700954 // TODO: perhaps add a query to filters so we might preserve opaqueness...
955 if (paint->getImageFilter() || paint->getColorFilter()) {
956 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -0700957 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +0000958 }
959 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000960 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
961 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962
reedb2db8982014-11-13 12:41:02 -0800963 SkBaseDevice* device = this->getTopDevice();
964 if (NULL == device) {
965 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800966 return;
reed@google.com76dd2772012-01-05 21:15:07 +0000967 }
reedb2db8982014-11-13 12:41:02 -0800968
reed76033be2015-03-14 10:54:31 -0700969 SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed8dc0ccb2015-03-20 06:32:52 -0700970 device = device->onCreateDevice(SkBaseDevice::CreateInfo(info, usage, geo), paint);
bungeman@google.come25c6842011-08-17 14:53:54 +0000971 if (NULL == device) {
joshualitt5f5a8d72015-02-25 14:09:45 -0800972 SkErrorInternals::SetError( kInternalError_SkError,
973 "Unable to create device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800974 return;
bungeman@google.come25c6842011-08-17 14:53:54 +0000975 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000976
reed@google.com6f8f2922011-03-04 22:27:10 +0000977 device->setOrigin(ir.fLeft, ir.fTop);
reed96e657d2015-03-10 17:30:07 -0700978 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 device->unref();
980
981 layer->fNext = fMCRec->fTopLayer;
982 fMCRec->fLayer = layer;
983 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984}
985
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000986int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
987 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
988}
989
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
991 SaveFlags flags) {
992 if (0xFF == alpha) {
993 return this->saveLayer(bounds, NULL, flags);
994 } else {
995 SkPaint tmpPaint;
996 tmpPaint.setAlpha(alpha);
997 return this->saveLayer(bounds, &tmpPaint, flags);
998 }
999}
1000
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001void SkCanvas::internalRestore() {
1002 SkASSERT(fMCStack.count() != 0);
1003
1004 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001005 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006
reed687fa1c2015-04-07 08:00:56 -07001007 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001008
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001009 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 DeviceCM* layer = fMCRec->fLayer; // may be null
1011 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1012 fMCRec->fLayer = NULL;
1013
1014 // now do the normal restore()
1015 fMCRec->~MCRec(); // balanced in save()
1016 fMCStack.pop_back();
1017 fMCRec = (MCRec*)fMCStack.back();
1018
1019 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1020 since if we're being recorded, we don't want to record this (the
1021 recorder will have already recorded the restore).
1022 */
bsalomon49f085d2014-09-05 13:34:00 -07001023 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001025 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001026 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1027 layer->fPaint);
1028 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 fDeviceCMDirty = true;
reedb679ca82015-04-07 04:40:48 -07001030 SkDELETE(layer);
1031 } else {
1032 // we're at the root
1033 SkASSERT(layer == (void*)fBaseLayerStorage);
1034 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001036 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037}
1038
reed4a8126e2014-09-22 07:29:03 -07001039SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1040 if (NULL == props) {
1041 props = &fProps;
1042 }
1043 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001044}
1045
reed4a8126e2014-09-22 07:29:03 -07001046SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001047 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001048 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001049}
1050
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001051SkImageInfo SkCanvas::imageInfo() const {
1052 SkBaseDevice* dev = this->getDevice();
1053 if (dev) {
1054 return dev->imageInfo();
1055 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001056 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001057 }
1058}
1059
1060const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1061 return this->onPeekPixels(info, rowBytes);
1062}
1063
1064const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1065 SkBaseDevice* dev = this->getDevice();
1066 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1067}
1068
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001069void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1070 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1071 if (pixels && origin) {
1072 *origin = this->getTopDevice(false)->getOrigin();
1073 }
1074 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001075}
1076
1077void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1078 SkBaseDevice* dev = this->getTopDevice();
1079 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1080}
1081
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001082SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1083 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1084 if (NULL == fAddr) {
1085 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001086 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001087 return; // failure, fAddr is NULL
1088 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001089 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1090 return; // failure, fAddr is NULL
1091 }
1092 fAddr = fBitmap.getPixels();
1093 fRowBytes = fBitmap.rowBytes();
1094 }
1095 SkASSERT(fAddr); // success
1096}
1097
1098bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1099 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001100 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001101 } else {
1102 bitmap->reset();
1103 return false;
1104 }
1105}
1106
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001108void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001110 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 return;
1112 }
1113
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001114 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001116 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001118
1119 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001120
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001121 SkRect storage;
1122 const SkRect* bounds = NULL;
1123 if (paint && paint->canComputeFastBounds()) {
1124 bitmap.getBounds(&storage);
1125 matrix.mapRect(&storage);
1126 bounds = &paint->computeFastBounds(storage, &storage);
1127 }
1128
1129 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001130
1131 while (iter.next()) {
1132 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1133 }
1134
1135 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136}
1137
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001138void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001139 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 SkPaint tmp;
1141 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 paint = &tmp;
1143 }
reed@google.com4b226022011-01-11 18:32:13 +00001144
reed@google.com8926b162012-03-23 15:36:36 +00001145 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001147 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001148 paint = &looper.paint();
1149 SkImageFilter* filter = paint->getImageFilter();
1150 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001151 if (filter && !dstDev->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001152 SkDeviceImageFilterProxy proxy(dstDev, fProps);
reed@google.com76dd2772012-01-05 21:15:07 +00001153 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001154 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001155 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001156 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001157 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001158 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001159 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001160 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001161 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001162 SkPaint tmpUnfiltered(*paint);
1163 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001164 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1165 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001166 }
1167 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001168 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001169 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001171 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172}
1173
reed41af9662015-01-05 07:49:08 -08001174void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001175 if (gTreatSpriteAsBitmap) {
1176 this->save();
1177 this->resetMatrix();
1178 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1179 this->restore();
1180 return;
1181 }
1182
danakj9881d632014-11-26 12:41:06 -08001183 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001184 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001185 return;
1186 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001187 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001188
reed@google.com8926b162012-03-23 15:36:36 +00001189 SkPaint tmp;
1190 if (NULL == paint) {
1191 paint = &tmp;
1192 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001193
reed@google.com8926b162012-03-23 15:36:36 +00001194 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001195
reed@google.com8926b162012-03-23 15:36:36 +00001196 while (iter.next()) {
1197 paint = &looper.paint();
1198 SkImageFilter* filter = paint->getImageFilter();
1199 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1200 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001201 SkDeviceImageFilterProxy proxy(iter.fDevice, fProps);
reed@google.com8926b162012-03-23 15:36:36 +00001202 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001203 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001204 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001205 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001206 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001207 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001208 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001209 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001210 SkPaint tmpUnfiltered(*paint);
1211 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001212 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001213 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001214 }
1215 } else {
1216 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1217 }
1218 }
1219 LOOPER_END
1220}
1221
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001223void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001224 SkMatrix m;
1225 m.setTranslate(dx, dy);
1226 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227}
1228
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001229void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001230 SkMatrix m;
1231 m.setScale(sx, sy);
1232 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233}
1234
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001235void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001236 SkMatrix m;
1237 m.setRotate(degrees);
1238 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239}
1240
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001241void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001242 SkMatrix m;
1243 m.setSkew(sx, sy);
1244 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001245}
1246
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001247void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001248 if (matrix.isIdentity()) {
1249 return;
1250 }
1251
reed2ff1fce2014-12-11 07:07:37 -08001252 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001254 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001255 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001256
1257 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001258}
1259
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260void SkCanvas::setMatrix(const SkMatrix& matrix) {
reed2ff1fce2014-12-11 07:07:37 -08001261 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001263 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001264 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001265 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266}
1267
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268void SkCanvas::resetMatrix() {
1269 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001270
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 matrix.reset();
1272 this->setMatrix(matrix);
1273}
1274
1275//////////////////////////////////////////////////////////////////////////////
1276
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001277void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001278 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001279 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1280 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001281}
1282
1283void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001284#ifdef SK_ENABLE_CLIP_QUICKREJECT
1285 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001286 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001287 return false;
1288 }
1289
reed@google.com3b3e8952012-08-16 20:53:31 +00001290 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001291 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001292 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001293
reed687fa1c2015-04-07 08:00:56 -07001294 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001295 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001296 }
1297 }
1298#endif
1299
reed@google.com5c3d1472011-02-22 19:12:23 +00001300 AutoValidateClip avc(this);
1301
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001303 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001304 if (!fAllowSoftClip) {
1305 edgeStyle = kHard_ClipEdgeStyle;
1306 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307
reed1f836ee2014-07-07 07:49:34 -07001308 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001309 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001310 // the matrix. This means we don't have to a) make a path, and b) tell
1311 // the region code to scan-convert the path, only to discover that it
1312 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314
reed1f836ee2014-07-07 07:49:34 -07001315 fMCRec->fMatrix.mapRect(&r, rect);
reed687fa1c2015-04-07 08:00:56 -07001316 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001317 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001319 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001320 // and clip against that, since it can handle any matrix. However, to
1321 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1322 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 SkPath path;
1324
1325 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001326 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 }
1328}
1329
reed73e714e2014-09-04 09:02:23 -07001330static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1331 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001332 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001333}
1334
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001335void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001336 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001337 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001338 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001339 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1340 } else {
1341 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001342 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001343}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001344
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001345void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001346 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001347 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001348 AutoValidateClip avc(this);
1349
1350 fDeviceCMDirty = true;
1351 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001352 if (!fAllowSoftClip) {
1353 edgeStyle = kHard_ClipEdgeStyle;
1354 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001355
reed687fa1c2015-04-07 08:00:56 -07001356 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001357
1358 SkPath devPath;
1359 devPath.addRRect(transformedRRect);
1360
reed73e714e2014-09-04 09:02:23 -07001361 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001362 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001363 }
1364
1365 SkPath path;
1366 path.addRRect(rrect);
1367 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001368 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001369}
1370
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001371void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001372 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001373 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1374 SkRect r;
1375 if (!path.isInverseFillType() && path.isRect(&r)) {
1376 this->onClipRect(r, op, edgeStyle);
1377 } else {
1378 this->onClipPath(path, op, edgeStyle);
1379 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380}
1381
1382void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001383#ifdef SK_ENABLE_CLIP_QUICKREJECT
1384 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001385 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001386 return false;
1387 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001388
reed@google.com3b3e8952012-08-16 20:53:31 +00001389 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001390 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001391 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001392
reed687fa1c2015-04-07 08:00:56 -07001393 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001394 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001395 }
1396 }
1397#endif
1398
reed@google.com5c3d1472011-02-22 19:12:23 +00001399 AutoValidateClip avc(this);
1400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001402 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001403 if (!fAllowSoftClip) {
1404 edgeStyle = kHard_ClipEdgeStyle;
1405 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
1407 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001408 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409
reed@google.comfe701122011-11-08 19:41:23 +00001410 // Check if the transfomation, or the original path itself
1411 // made us empty. Note this can also happen if we contained NaN
1412 // values. computing the bounds detects this, and will set our
1413 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1414 if (devPath.getBounds().isEmpty()) {
1415 // resetting the path will remove any NaN or other wanky values
1416 // that might upset our scan converter.
1417 devPath.reset();
1418 }
1419
reed@google.com5c3d1472011-02-22 19:12:23 +00001420 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001421 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001422
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001423 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001424 bool clipIsAA = getClipStack()->asPath(&devPath);
1425 if (clipIsAA) {
1426 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001427 }
fmalita1a481fe2015-02-04 07:39:34 -08001428
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001429 op = SkRegion::kReplace_Op;
1430 }
1431
reed73e714e2014-09-04 09:02:23 -07001432 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433}
1434
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001435void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001436 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001437 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001438}
1439
1440void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001441 AutoValidateClip avc(this);
1442
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001444 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445
reed@google.com5c3d1472011-02-22 19:12:23 +00001446 // todo: signal fClipStack that we have a region, and therefore (I guess)
1447 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001448 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001449
reed1f836ee2014-07-07 07:49:34 -07001450 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
reed@google.com819c9212011-02-23 18:56:55 +00001453#ifdef SK_DEBUG
1454void SkCanvas::validateClip() const {
1455 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001456 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001457 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001458 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001459 return;
1460 }
1461
reed@google.com819c9212011-02-23 18:56:55 +00001462 SkIRect ir;
1463 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001464 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001465
reed687fa1c2015-04-07 08:00:56 -07001466 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001467 const SkClipStack::Element* element;
1468 while ((element = iter.next()) != NULL) {
1469 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001470 case SkClipStack::Element::kRect_Type:
1471 element->getRect().round(&ir);
1472 tmpClip.op(ir, element->getOp());
1473 break;
1474 case SkClipStack::Element::kEmpty_Type:
1475 tmpClip.setEmpty();
1476 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001477 default: {
1478 SkPath path;
1479 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001480 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001481 break;
1482 }
reed@google.com819c9212011-02-23 18:56:55 +00001483 }
1484 }
reed@google.com819c9212011-02-23 18:56:55 +00001485}
1486#endif
1487
reed@google.com90c07ea2012-04-13 13:50:27 +00001488void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001489 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001490 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001491
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001492 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001493 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001494 }
1495}
1496
reed@google.com5c3d1472011-02-22 19:12:23 +00001497///////////////////////////////////////////////////////////////////////////////
1498
reed@google.com754de5f2014-02-24 19:38:20 +00001499bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001500 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001501}
1502
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001503bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001504 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001505}
1506
reed@google.com3b3e8952012-08-16 20:53:31 +00001507bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001508 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001509 return true;
1510
reed1f836ee2014-07-07 07:49:34 -07001511 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512 return true;
1513 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514
reed1f836ee2014-07-07 07:49:34 -07001515 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001516 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001517 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001518 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001519 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001520 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001521
reed@android.coma380ae42009-07-21 01:17:02 +00001522 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001523 // TODO: should we use | instead, or compare all 4 at once?
1524 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001525 return true;
1526 }
reed@google.comc0784db2013-12-13 21:16:12 +00001527 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001528 return true;
1529 }
1530 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532}
1533
reed@google.com3b3e8952012-08-16 20:53:31 +00001534bool SkCanvas::quickReject(const SkPath& path) const {
1535 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536}
1537
reed@google.com3b3e8952012-08-16 20:53:31 +00001538bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001539 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001540 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541 return false;
1542 }
1543
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001544 SkMatrix inverse;
1545 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001546 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001547 if (bounds) {
1548 bounds->setEmpty();
1549 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001550 return false;
1551 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552
bsalomon49f085d2014-09-05 13:34:00 -07001553 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001554 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001555 // adjust it outwards in case we are antialiasing
1556 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001557
reed@google.com8f4d2302013-12-17 16:44:46 +00001558 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1559 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001560 inverse.mapRect(bounds, r);
1561 }
1562 return true;
1563}
1564
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001565bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001566 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001567 if (clip.isEmpty()) {
1568 if (bounds) {
1569 bounds->setEmpty();
1570 }
1571 return false;
1572 }
1573
bsalomon49f085d2014-09-05 13:34:00 -07001574 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001575 *bounds = clip.getBounds();
1576 }
1577 return true;
1578}
1579
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001581 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582}
1583
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001584const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001585 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001586}
1587
reed@google.com9c135db2014-03-12 18:28:35 +00001588GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1589 SkBaseDevice* dev = this->getTopDevice();
1590 return dev ? dev->accessRenderTarget() : NULL;
1591}
1592
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001593GrContext* SkCanvas::getGrContext() {
1594#if SK_SUPPORT_GPU
1595 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001596 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001597 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001598 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001599 return renderTarget->getContext();
1600 }
1601 }
1602#endif
1603
1604 return NULL;
1605
1606}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001607
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001608void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1609 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001610 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001611 if (outer.isEmpty()) {
1612 return;
1613 }
1614 if (inner.isEmpty()) {
1615 this->drawRRect(outer, paint);
1616 return;
1617 }
1618
1619 // We don't have this method (yet), but technically this is what we should
1620 // be able to assert...
1621 // SkASSERT(outer.contains(inner));
1622 //
1623 // For now at least check for containment of bounds
1624 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1625
1626 this->onDrawDRRect(outer, inner, paint);
1627}
1628
reed41af9662015-01-05 07:49:08 -08001629// These need to stop being virtual -- clients need to override the onDraw... versions
1630
1631void SkCanvas::drawPaint(const SkPaint& paint) {
1632 this->onDrawPaint(paint);
1633}
1634
1635void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1636 this->onDrawRect(r, paint);
1637}
1638
1639void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1640 this->onDrawOval(r, paint);
1641}
1642
1643void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1644 this->onDrawRRect(rrect, paint);
1645}
1646
1647void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1648 this->onDrawPoints(mode, count, pts, paint);
1649}
1650
1651void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1652 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1653 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1654 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1655 indices, indexCount, paint);
1656}
1657
1658void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1659 this->onDrawPath(path, paint);
1660}
1661
1662void SkCanvas::drawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
1663 this->onDrawImage(image, dx, dy, paint);
1664}
1665
1666void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1667 const SkPaint* paint) {
1668 this->onDrawImageRect(image, src, dst, paint);
1669}
1670
1671void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
tomhudson2df6fd62015-04-09 09:20:19 -07001672 if (bitmap.empty()) {
1673 return;
1674 }
reed41af9662015-01-05 07:49:08 -08001675 this->onDrawBitmap(bitmap, dx, dy, paint);
1676}
1677
1678void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1679 const SkPaint* paint, DrawBitmapRectFlags flags) {
tomhudson2df6fd62015-04-09 09:20:19 -07001680 if (bitmap.empty()) {
1681 return;
1682 }
reed41af9662015-01-05 07:49:08 -08001683 this->onDrawBitmapRect(bitmap, src, dst, paint, flags);
1684}
1685
1686void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1687 const SkPaint* paint) {
tomhudson2df6fd62015-04-09 09:20:19 -07001688 if (bitmap.empty()) {
1689 return;
1690 }
reed41af9662015-01-05 07:49:08 -08001691 this->onDrawBitmapNine(bitmap, center, dst, paint);
1692}
1693
1694void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
tomhudson2df6fd62015-04-09 09:20:19 -07001695 if (bitmap.empty()) {
1696 return;
1697 }
reed41af9662015-01-05 07:49:08 -08001698 this->onDrawSprite(bitmap, left, top, paint);
1699}
1700
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701//////////////////////////////////////////////////////////////////////////////
1702// These are the virtual drawing methods
1703//////////////////////////////////////////////////////////////////////////////
1704
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001705void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001706 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001707 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1708 }
1709}
1710
reed41af9662015-01-05 07:49:08 -08001711void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001712 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001713 this->internalDrawPaint(paint);
1714}
1715
1716void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001717 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718
1719 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001720 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721 }
1722
reed@google.com4e2b3d32011-04-07 14:18:59 +00001723 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724}
1725
reed41af9662015-01-05 07:49:08 -08001726void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1727 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001728 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729 if ((long)count <= 0) {
1730 return;
1731 }
1732
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001733 SkRect r, storage;
1734 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001735 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001736 // special-case 2 points (common for drawing a single line)
1737 if (2 == count) {
1738 r.set(pts[0], pts[1]);
1739 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001740 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001741 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001742 bounds = &paint.computeFastStrokeBounds(r, &storage);
1743 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001744 return;
1745 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001746 }
reed@google.coma584aed2012-05-16 14:06:02 +00001747
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 SkASSERT(pts != NULL);
1749
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001750 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001751
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001753 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 }
reed@google.com4b226022011-01-11 18:32:13 +00001755
reed@google.com4e2b3d32011-04-07 14:18:59 +00001756 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757}
1758
reed41af9662015-01-05 07:49:08 -08001759void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001760 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001761 SkRect storage;
1762 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08001764 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
1765 // To prevent accidental rejecting at this stage, we have to sort it before we check.
1766 SkRect tmp(r);
1767 tmp.sort();
1768
1769 bounds = &paint.computeFastBounds(tmp, &storage);
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001770 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 return;
1772 }
1773 }
reed@google.com4b226022011-01-11 18:32:13 +00001774
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001775 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776
1777 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001778 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 }
1780
reed@google.com4e2b3d32011-04-07 14:18:59 +00001781 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782}
1783
reed41af9662015-01-05 07:49:08 -08001784void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001785 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001786 SkRect storage;
1787 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001788 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001789 bounds = &paint.computeFastBounds(oval, &storage);
1790 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001791 return;
1792 }
1793 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001794
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001795 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001796
1797 while (iter.next()) {
1798 iter.fDevice->drawOval(iter, oval, looper.paint());
1799 }
1800
1801 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001802}
1803
reed41af9662015-01-05 07:49:08 -08001804void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001805 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001806 SkRect storage;
1807 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001808 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001809 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1810 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001811 return;
1812 }
1813 }
1814
1815 if (rrect.isRect()) {
1816 // call the non-virtual version
1817 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001818 return;
1819 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001820 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001821 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1822 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001823 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001824
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001825 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001826
1827 while (iter.next()) {
1828 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1829 }
1830
1831 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001832}
1833
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001834void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1835 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001836 SkRect storage;
1837 const SkRect* bounds = NULL;
1838 if (paint.canComputeFastBounds()) {
1839 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1840 if (this->quickReject(*bounds)) {
1841 return;
1842 }
1843 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001844
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001845 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001846
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001847 while (iter.next()) {
1848 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1849 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001850
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001851 LOOPER_END
1852}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001853
reed41af9662015-01-05 07:49:08 -08001854void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001855 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001856 if (!path.isFinite()) {
1857 return;
1858 }
1859
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001860 SkRect storage;
1861 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001862 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001863 const SkRect& pathBounds = path.getBounds();
1864 bounds = &paint.computeFastBounds(pathBounds, &storage);
1865 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866 return;
1867 }
1868 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001869
1870 const SkRect& r = path.getBounds();
1871 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001872 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001873 this->internalDrawPaint(paint);
1874 }
1875 return;
1876 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001877
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001878 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001879
1880 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001881 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 }
1883
reed@google.com4e2b3d32011-04-07 14:18:59 +00001884 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885}
1886
kkinnunena9baa652015-03-05 06:33:54 -08001887void SkCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001888 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
kkinnunena9baa652015-03-05 06:33:54 -08001889 image->draw(this, dx, dy, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001890}
1891
reed41af9662015-01-05 07:49:08 -08001892void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1893 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001894 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
kkinnunena9baa652015-03-05 06:33:54 -08001895 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001896}
1897
reed41af9662015-01-05 07:49:08 -08001898void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001899 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900 SkDEBUGCODE(bitmap.validate();)
1901
reed@google.com3d608122011-11-21 15:16:16 +00001902 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001903 SkRect bounds = {
1904 x, y,
1905 x + SkIntToScalar(bitmap.width()),
1906 y + SkIntToScalar(bitmap.height())
1907 };
1908 if (paint) {
1909 (void)paint->computeFastBounds(bounds, &bounds);
1910 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001911 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912 return;
1913 }
1914 }
reed@google.com4b226022011-01-11 18:32:13 +00001915
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916 SkMatrix matrix;
1917 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001918 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919}
1920
reed@google.com9987ec32011-09-07 11:57:52 +00001921// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001922void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001923 const SkRect& dst, const SkPaint* paint,
1924 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001925 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 return;
1927 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001928
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001929 SkRect storage;
1930 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001931 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001932 if (paint) {
1933 bounds = &paint->computeFastBounds(dst, &storage);
1934 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001935 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001936 return;
1937 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 }
reed@google.com3d608122011-11-21 15:16:16 +00001939
reed@google.com33535f32012-09-25 15:37:50 +00001940 SkLazyPaint lazy;
1941 if (NULL == paint) {
1942 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001943 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001944
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001945 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001946
reed@google.com33535f32012-09-25 15:37:50 +00001947 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001948 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001949 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001950
reed@google.com33535f32012-09-25 15:37:50 +00001951 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952}
1953
reed41af9662015-01-05 07:49:08 -08001954void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1955 const SkPaint* paint, DrawBitmapRectFlags flags) {
danakj9881d632014-11-26 12:41:06 -08001956 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001957 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001958 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001959}
1960
reed@google.com9987ec32011-09-07 11:57:52 +00001961void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1962 const SkIRect& center, const SkRect& dst,
1963 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001964 if (bitmap.drawsNothing()) {
1965 return;
1966 }
reed@google.com3d608122011-11-21 15:16:16 +00001967 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001968 SkRect storage;
1969 const SkRect* bounds = &dst;
1970 if (paint) {
1971 bounds = &paint->computeFastBounds(dst, &storage);
1972 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001973 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001974 return;
1975 }
1976 }
1977
reed@google.com9987ec32011-09-07 11:57:52 +00001978 const int32_t w = bitmap.width();
1979 const int32_t h = bitmap.height();
1980
1981 SkIRect c = center;
1982 // pin center to the bounds of the bitmap
1983 c.fLeft = SkMax32(0, center.fLeft);
1984 c.fTop = SkMax32(0, center.fTop);
1985 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1986 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1987
reed@google.com71121732012-09-18 15:14:33 +00001988 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001989 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001990 };
1991 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001992 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001993 };
reed@google.com9987ec32011-09-07 11:57:52 +00001994 SkScalar dstX[4] = {
1995 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1996 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1997 };
1998 SkScalar dstY[4] = {
1999 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2000 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2001 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002002
reed@google.com9987ec32011-09-07 11:57:52 +00002003 if (dstX[1] > dstX[2]) {
2004 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2005 dstX[2] = dstX[1];
2006 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002007
reed@google.com9987ec32011-09-07 11:57:52 +00002008 if (dstY[1] > dstY[2]) {
2009 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2010 dstY[2] = dstY[1];
2011 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002012
reed@google.com9987ec32011-09-07 11:57:52 +00002013 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002014 SkRect s, d;
2015
reed@google.com9987ec32011-09-07 11:57:52 +00002016 s.fTop = srcY[y];
2017 s.fBottom = srcY[y+1];
2018 d.fTop = dstY[y];
2019 d.fBottom = dstY[y+1];
2020 for (int x = 0; x < 3; x++) {
2021 s.fLeft = srcX[x];
2022 s.fRight = srcX[x+1];
2023 d.fLeft = dstX[x];
2024 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002025 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002026 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002027 }
2028 }
2029}
2030
reed41af9662015-01-05 07:49:08 -08002031void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2032 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002033 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002034 SkDEBUGCODE(bitmap.validate();)
2035
2036 // Need a device entry-point, so gpu can use a mesh
2037 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2038}
2039
reed@google.comf67e4cf2011-03-15 20:56:58 +00002040class SkDeviceFilteredPaint {
2041public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002042 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002043 uint32_t filteredFlags = device->filterTextFlags(paint);
2044 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002045 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002046 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002047 fPaint = newPaint;
2048 } else {
2049 fPaint = &paint;
2050 }
2051 }
2052
reed@google.comf67e4cf2011-03-15 20:56:58 +00002053 const SkPaint& paint() const { return *fPaint; }
2054
2055private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002056 const SkPaint* fPaint;
2057 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002058};
2059
bungeman@google.com52c748b2011-08-22 21:30:43 +00002060void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2061 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002062 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002063 draw.fDevice->drawRect(draw, r, paint);
2064 } else {
2065 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002066 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002067 draw.fDevice->drawRect(draw, r, p);
2068 }
2069}
2070
2071void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2072 const char text[], size_t byteLength,
2073 SkScalar x, SkScalar y) {
2074 SkASSERT(byteLength == 0 || text != NULL);
2075
2076 // nothing to draw
2077 if (text == NULL || byteLength == 0 ||
2078 draw.fClip->isEmpty() ||
2079 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2080 return;
2081 }
2082
2083 SkScalar width = 0;
2084 SkPoint start;
2085
2086 start.set(0, 0); // to avoid warning
2087 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2088 SkPaint::kStrikeThruText_Flag)) {
2089 width = paint.measureText(text, byteLength);
2090
2091 SkScalar offsetX = 0;
2092 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2093 offsetX = SkScalarHalf(width);
2094 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2095 offsetX = width;
2096 }
2097 start.set(x - offsetX, y);
2098 }
2099
2100 if (0 == width) {
2101 return;
2102 }
2103
2104 uint32_t flags = paint.getFlags();
2105
2106 if (flags & (SkPaint::kUnderlineText_Flag |
2107 SkPaint::kStrikeThruText_Flag)) {
2108 SkScalar textSize = paint.getTextSize();
2109 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2110 SkRect r;
2111
2112 r.fLeft = start.fX;
2113 r.fRight = start.fX + width;
2114
2115 if (flags & SkPaint::kUnderlineText_Flag) {
2116 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2117 start.fY);
2118 r.fTop = offset;
2119 r.fBottom = offset + height;
2120 DrawRect(draw, paint, r, textSize);
2121 }
2122 if (flags & SkPaint::kStrikeThruText_Flag) {
2123 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2124 start.fY);
2125 r.fTop = offset;
2126 r.fBottom = offset + height;
2127 DrawRect(draw, paint, r, textSize);
2128 }
2129 }
2130}
2131
reed@google.come0d9ce82014-04-23 04:00:17 +00002132void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2133 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002134 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135
2136 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002137 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002138 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002139 DrawTextDecorations(iter, dfp.paint(),
2140 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141 }
2142
reed@google.com4e2b3d32011-04-07 14:18:59 +00002143 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002144}
2145
reed@google.come0d9ce82014-04-23 04:00:17 +00002146void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2147 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002148 SkPoint textOffset = SkPoint::Make(0, 0);
2149
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002150 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002151
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002153 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002154 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002155 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002157
reed@google.com4e2b3d32011-04-07 14:18:59 +00002158 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159}
2160
reed@google.come0d9ce82014-04-23 04:00:17 +00002161void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2162 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002163
2164 SkPoint textOffset = SkPoint::Make(0, constY);
2165
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002166 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002167
reed@android.com8a1c16f2008-12-17 15:59:43 +00002168 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002169 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002170 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002171 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002173
reed@google.com4e2b3d32011-04-07 14:18:59 +00002174 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175}
2176
reed@google.come0d9ce82014-04-23 04:00:17 +00002177void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2178 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002179 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002180
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 while (iter.next()) {
2182 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002183 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002185
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002186 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002187}
2188
fmalita00d5c2c2014-08-21 08:53:26 -07002189void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2190 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002191
fmalita85d5eb92015-03-04 11:20:12 -08002192 SkRect storage;
2193 const SkRect* bounds = NULL;
fmalita19653d12014-10-16 11:53:30 -07002194 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002195 storage = blob->bounds().makeOffset(x, y);
2196 bounds = &paint.computeFastBounds(storage, &storage);
fmalita7ba7aa72014-08-29 09:46:36 -07002197
fmalita85d5eb92015-03-04 11:20:12 -08002198 if (this->quickReject(*bounds)) {
fmalita7ba7aa72014-08-29 09:46:36 -07002199 return;
2200 }
2201 }
2202
fmalita024f9962015-03-03 19:08:17 -08002203 // We cannot filter in the looper as we normally do, because the paint is
2204 // incomplete at this point (text-related attributes are embedded within blob run paints).
2205 SkDrawFilter* drawFilter = fMCRec->fFilter;
2206 fMCRec->fFilter = NULL;
2207
fmalita85d5eb92015-03-04 11:20:12 -08002208 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002209
fmalitaaa1b9122014-08-28 14:32:24 -07002210 while (iter.next()) {
2211 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002212 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002213 }
2214
fmalitaaa1b9122014-08-28 14:32:24 -07002215 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002216
2217 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002218}
2219
reed@google.come0d9ce82014-04-23 04:00:17 +00002220// These will become non-virtual, so they always call the (virtual) onDraw... method
2221void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2222 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002223 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002224 this->onDrawText(text, byteLength, x, y, paint);
2225}
2226void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2227 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002228 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002229 this->onDrawPosText(text, byteLength, pos, paint);
2230}
2231void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2232 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002233 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002234 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2235}
2236void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2237 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002238 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002239 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2240}
fmalita00d5c2c2014-08-21 08:53:26 -07002241void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2242 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002243 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002244 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002245 this->onDrawTextBlob(blob, x, y, paint);
2246 }
2247}
reed@google.come0d9ce82014-04-23 04:00:17 +00002248
reed41af9662015-01-05 07:49:08 -08002249void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2250 const SkPoint verts[], const SkPoint texs[],
2251 const SkColor colors[], SkXfermode* xmode,
2252 const uint16_t indices[], int indexCount,
2253 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002254 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002255 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002256
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257 while (iter.next()) {
2258 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002259 colors, xmode, indices, indexCount,
2260 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 }
reed@google.com4b226022011-01-11 18:32:13 +00002262
reed@google.com4e2b3d32011-04-07 14:18:59 +00002263 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264}
2265
dandovb3c9d1c2014-08-12 08:34:29 -07002266void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2267 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002268 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002269 if (NULL == cubics) {
2270 return;
2271 }
mtklein6cfa73a2014-08-13 13:33:49 -07002272
dandovecfff212014-08-04 10:02:00 -07002273 // Since a patch is always within the convex hull of the control points, we discard it when its
2274 // bounding rectangle is completely outside the current clip.
2275 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002276 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002277 if (this->quickReject(bounds)) {
2278 return;
2279 }
mtklein6cfa73a2014-08-13 13:33:49 -07002280
dandovb3c9d1c2014-08-12 08:34:29 -07002281 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2282}
2283
2284void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2285 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2286
dandovecfff212014-08-04 10:02:00 -07002287 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002288
dandovecfff212014-08-04 10:02:00 -07002289 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002290 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002291 }
mtklein6cfa73a2014-08-13 13:33:49 -07002292
dandovecfff212014-08-04 10:02:00 -07002293 LOOPER_END
2294}
2295
reed3cb38402015-02-06 08:36:15 -08002296void SkCanvas::drawDrawable(SkDrawable* dr) {
reed6be2aa92014-11-18 11:08:05 -08002297 if (dr && !this->quickReject(dr->getBounds())) {
2298 this->onDrawDrawable(dr);
reed6a070dc2014-11-11 19:36:09 -08002299 }
2300}
2301
reed3cb38402015-02-06 08:36:15 -08002302void SkCanvas::onDrawDrawable(SkDrawable* dr) {
reed6a070dc2014-11-11 19:36:09 -08002303 dr->draw(this);
2304}
2305
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306//////////////////////////////////////////////////////////////////////////////
2307// These methods are NOT virtual, and therefore must call back into virtual
2308// methods, rather than actually drawing themselves.
2309//////////////////////////////////////////////////////////////////////////////
2310
2311void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002312 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002313 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314 SkPaint paint;
2315
2316 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002317 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002318 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002319 }
2320 this->drawPaint(paint);
2321}
2322
reed@android.com845fdac2009-06-23 03:01:32 +00002323void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002324 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325 SkPaint paint;
2326
2327 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002328 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002329 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002330 }
2331 this->drawPaint(paint);
2332}
2333
2334void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002335 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002337
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338 pt.set(x, y);
2339 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2340}
2341
2342void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002343 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 SkPoint pt;
2345 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002346
reed@android.com8a1c16f2008-12-17 15:59:43 +00002347 pt.set(x, y);
2348 paint.setColor(color);
2349 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2350}
2351
2352void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2353 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002354 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002355 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002356
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 pts[0].set(x0, y0);
2358 pts[1].set(x1, y1);
2359 this->drawPoints(kLines_PointMode, 2, pts, paint);
2360}
2361
2362void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2363 SkScalar right, SkScalar bottom,
2364 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002365 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 SkRect r;
2367
2368 r.set(left, top, right, bottom);
2369 this->drawRect(r, paint);
2370}
2371
2372void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2373 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002374 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002375 if (radius < 0) {
2376 radius = 0;
2377 }
2378
2379 SkRect r;
2380 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002381 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382}
2383
2384void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2385 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002386 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387 if (rx > 0 && ry > 0) {
2388 if (paint.canComputeFastBounds()) {
2389 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002390 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002391 return;
2392 }
2393 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002394 SkRRect rrect;
2395 rrect.setRectXY(r, rx, ry);
2396 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002397 } else {
2398 this->drawRect(r, paint);
2399 }
2400}
2401
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2403 SkScalar sweepAngle, bool useCenter,
2404 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002405 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002406 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2407 this->drawOval(oval, paint);
2408 } else {
2409 SkPath path;
2410 if (useCenter) {
2411 path.moveTo(oval.centerX(), oval.centerY());
2412 }
2413 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2414 if (useCenter) {
2415 path.close();
2416 }
2417 this->drawPath(path, paint);
2418 }
2419}
2420
2421void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2422 const SkPath& path, SkScalar hOffset,
2423 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002424 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002425 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002426
reed@android.com8a1c16f2008-12-17 15:59:43 +00002427 matrix.setTranslate(hOffset, vOffset);
2428 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2429}
2430
reed@android.comf76bacf2009-05-13 14:00:33 +00002431///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002432void SkCanvas::drawPicture(const SkPicture* picture) {
danakj9881d632014-11-26 12:41:06 -08002433 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002434 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002435 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002436 }
2437}
2438
reedd5fa1a42014-08-09 11:08:05 -07002439void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002440 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002441 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002442 if (matrix && matrix->isIdentity()) {
2443 matrix = NULL;
2444 }
2445 this->onDrawPicture(picture, matrix, paint);
2446 }
2447}
robertphillips9b14f262014-06-04 05:40:44 -07002448
reedd5fa1a42014-08-09 11:08:05 -07002449void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2450 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002451 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002452 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002453 // Canvas has to first give the device the opportunity to render
2454 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002455 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002456 return; // the device has rendered the entire picture
2457 }
2458 }
2459
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002460 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002461
robertphillipsc5ba71d2014-09-04 08:42:50 -07002462 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002463}
2464
reed@android.com8a1c16f2008-12-17 15:59:43 +00002465///////////////////////////////////////////////////////////////////////////////
2466///////////////////////////////////////////////////////////////////////////////
2467
2468SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002469 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002470
2471 SkASSERT(canvas);
2472
2473 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2474 fDone = !fImpl->next();
2475}
2476
2477SkCanvas::LayerIter::~LayerIter() {
2478 fImpl->~SkDrawIter();
2479}
2480
2481void SkCanvas::LayerIter::next() {
2482 fDone = !fImpl->next();
2483}
2484
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002485SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002486 return fImpl->getDevice();
2487}
2488
2489const SkMatrix& SkCanvas::LayerIter::matrix() const {
2490 return fImpl->getMatrix();
2491}
2492
2493const SkPaint& SkCanvas::LayerIter::paint() const {
2494 const SkPaint* paint = fImpl->getPaint();
2495 if (NULL == paint) {
2496 paint = &fDefaultPaint;
2497 }
2498 return *paint;
2499}
2500
2501const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2502int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2503int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002504
2505///////////////////////////////////////////////////////////////////////////////
2506
fmalitac3b589a2014-06-05 12:40:07 -07002507SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002508
2509///////////////////////////////////////////////////////////////////////////////
2510
2511static bool supported_for_raster_canvas(const SkImageInfo& info) {
2512 switch (info.alphaType()) {
2513 case kPremul_SkAlphaType:
2514 case kOpaque_SkAlphaType:
2515 break;
2516 default:
2517 return false;
2518 }
2519
2520 switch (info.colorType()) {
2521 case kAlpha_8_SkColorType:
2522 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002523 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002524 break;
2525 default:
2526 return false;
2527 }
2528
2529 return true;
2530}
2531
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002532SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2533 if (!supported_for_raster_canvas(info)) {
2534 return NULL;
2535 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002536
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002537 SkBitmap bitmap;
2538 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2539 return NULL;
2540 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002541 return SkNEW_ARGS(SkCanvas, (bitmap));
2542}
reedd5fa1a42014-08-09 11:08:05 -07002543
2544///////////////////////////////////////////////////////////////////////////////
2545
2546SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002547 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002548 : fCanvas(canvas)
2549 , fSaveCount(canvas->getSaveCount())
2550{
bsalomon49f085d2014-09-05 13:34:00 -07002551 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002552 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002553 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002554 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002555 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002556 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002557 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002558 canvas->save();
2559 }
mtklein6cfa73a2014-08-13 13:33:49 -07002560
bsalomon49f085d2014-09-05 13:34:00 -07002561 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002562 canvas->concat(*matrix);
2563 }
2564}
2565
2566SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2567 fCanvas->restoreToCount(fSaveCount);
2568}