blob: 68ced602f62da23dc3094de28326797c5502652b [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) {
reed51985e32015-04-11 08:04:56 -0700592 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
594 return filter;
595}
596
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000597SkMetaData& SkCanvas::getMetaData() {
598 // metadata users are rare, so we lazily allocate it. If that changes we
599 // can decide to just make it a field in the device (rather than a ptr)
600 if (NULL == fMetaData) {
601 fMetaData = new SkMetaData;
602 }
603 return *fMetaData;
604}
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606///////////////////////////////////////////////////////////////////////////////
607
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000608void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000609 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000610 if (device) {
611 device->flush();
612 }
613}
614
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000615SkISize SkCanvas::getTopLayerSize() const {
616 SkBaseDevice* d = this->getTopDevice();
617 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
618}
619
620SkIPoint SkCanvas::getTopLayerOrigin() const {
621 SkBaseDevice* d = this->getTopDevice();
622 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
623}
624
625SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000626 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000627 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
628}
629
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000630SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000632 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 SkASSERT(rec && rec->fLayer);
634 return rec->fLayer->fDevice;
635}
636
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000637SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000638 if (updateMatrixClip) {
639 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
640 }
reed@google.com9266fed2011-03-30 00:18:03 +0000641 return fMCRec->fTopLayer->fDevice;
642}
643
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000644bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
645 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
646 return false;
647 }
648
649 bool weAllocated = false;
650 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700651 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000652 return false;
653 }
654 weAllocated = true;
655 }
656
657 SkBitmap bm(*bitmap);
658 bm.lockPixels();
659 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
660 return true;
661 }
662
663 if (weAllocated) {
664 bitmap->setPixelRef(NULL);
665 }
666 return false;
667}
reed@google.com51df9e32010-12-23 19:29:18 +0000668
bsalomon@google.comc6980972011-11-02 19:57:21 +0000669bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000670 SkIRect r = srcRect;
671 const SkISize size = this->getBaseLayerSize();
672 if (!r.intersect(0, 0, size.width(), size.height())) {
673 bitmap->reset();
674 return false;
675 }
676
reed84825042014-09-02 12:50:45 -0700677 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000678 // bitmap will already be reset.
679 return false;
680 }
681 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
682 bitmap->reset();
683 return false;
684 }
685 return true;
686}
687
reed96472de2014-12-10 09:53:42 -0800688bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000689 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000690 if (!device) {
691 return false;
692 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000693 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800694
reed96472de2014-12-10 09:53:42 -0800695 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
696 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000697 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000698 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000699
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000700 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800701 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000702}
703
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000704bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
705 if (bitmap.getTexture()) {
706 return false;
707 }
708 SkBitmap bm(bitmap);
709 bm.lockPixels();
710 if (bm.getPixels()) {
711 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
712 }
713 return false;
714}
715
716bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
717 int x, int y) {
718 switch (origInfo.colorType()) {
719 case kUnknown_SkColorType:
720 case kIndex_8_SkColorType:
721 return false;
722 default:
723 break;
724 }
725 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
726 return false;
727 }
728
729 const SkISize size = this->getBaseLayerSize();
730 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
731 if (!target.intersect(0, 0, size.width(), size.height())) {
732 return false;
733 }
734
735 SkBaseDevice* device = this->getDevice();
736 if (!device) {
737 return false;
738 }
739
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000740 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700741 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000742
743 // if x or y are negative, then we have to adjust pixels
744 if (x > 0) {
745 x = 0;
746 }
747 if (y > 0) {
748 y = 0;
749 }
750 // here x,y are either 0 or negative
751 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
752
reed4af35f32014-06-27 17:47:49 -0700753 // Tell our owning surface to bump its generation ID
754 this->predrawNotify();
755
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000756 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000757 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000758}
reed@google.com51df9e32010-12-23 19:29:18 +0000759
junov@google.com4370aed2012-01-18 16:21:08 +0000760SkCanvas* SkCanvas::canvasForDrawIter() {
761 return this;
762}
763
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764//////////////////////////////////////////////////////////////////////////////
765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766void SkCanvas::updateDeviceCMCache() {
767 if (fDeviceCMDirty) {
768 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700769 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 if (NULL == layer->fNext) { // only one layer
reed687fa1c2015-04-07 08:00:56 -0700773 layer->updateMC(totalMatrix, totalClip, *fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000775 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 do {
reed687fa1c2015-04-07 08:00:56 -0700777 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 } while ((layer = layer->fNext) != NULL);
779 }
780 fDeviceCMDirty = false;
781 }
782}
783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784///////////////////////////////////////////////////////////////////////////////
785
reed2ff1fce2014-12-11 07:07:37 -0800786void SkCanvas::checkForDeferredSave() {
787 if (fMCRec->fDeferredSaveCount > 0) {
788 fMCRec->fDeferredSaveCount -= 1;
789 this->doSave();
790 }
791}
792
reedf0090cb2014-11-26 08:55:51 -0800793int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800794#ifdef SK_DEBUG
795 int count = 0;
796 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
797 for (;;) {
798 const MCRec* rec = (const MCRec*)iter.next();
799 if (!rec) {
800 break;
801 }
802 count += 1 + rec->fDeferredSaveCount;
803 }
804 SkASSERT(count == fSaveCount);
805#endif
806 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800807}
808
809int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800810 fSaveCount += 1;
811 fMCRec->fDeferredSaveCount += 1;
812 return this->getSaveCount() - 1; // return our prev value
813}
814
815void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800816 this->willSave();
reed2ff1fce2014-12-11 07:07:37 -0800817 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800818}
819
820void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800821 if (fMCRec->fDeferredSaveCount > 0) {
822 SkASSERT(fSaveCount > 1);
823 fSaveCount -= 1;
824 fMCRec->fDeferredSaveCount -= 1;
825 } else {
826 // check for underflow
827 if (fMCStack.count() > 1) {
828 this->willRestore();
829 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700830 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800831 this->internalRestore();
832 this->didRestore();
833 }
reedf0090cb2014-11-26 08:55:51 -0800834 }
835}
836
837void SkCanvas::restoreToCount(int count) {
838 // sanity check
839 if (count < 1) {
840 count = 1;
841 }
mtkleinf0f14112014-12-12 08:46:25 -0800842
reedf0090cb2014-11-26 08:55:51 -0800843 int n = this->getSaveCount() - count;
844 for (int i = 0; i < n; ++i) {
845 this->restore();
846 }
847}
848
reed2ff1fce2014-12-11 07:07:37 -0800849void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700851 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000853
reed687fa1c2015-04-07 08:00:56 -0700854 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855}
856
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000858#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000860#else
861 return true;
862#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863}
864
junov@chromium.orga907ac32012-02-24 21:54:07 +0000865bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -0700866 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000867 SkIRect clipBounds;
868 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000869 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000870 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000871
reed96e657d2015-03-10 17:30:07 -0700872 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
873
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000874 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -0700875 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000876 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000877 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700878 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000880
reed96e657d2015-03-10 17:30:07 -0700881 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 r.roundOut(&ir);
883 // early exit if the layer's bounds are clipped out
884 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000885 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -0700886 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -0700887 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000888 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000889 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 }
891 } else { // no user bounds, so just use the clip
892 ir = clipBounds;
893 }
reed180aec42015-03-11 10:39:04 -0700894 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000896 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -0700897 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -0700898 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -0700899 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -0700900 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000901 }
902
903 if (intersection) {
904 *intersection = ir;
905 }
906 return true;
907}
908
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000909int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -0800910 if (gIgnoreSaveLayerBounds) {
911 bounds = NULL;
912 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000913 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -0700914 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -0700915 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -0800916 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000917}
918
reed2ff1fce2014-12-11 07:07:37 -0800919int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -0800920 if (gIgnoreSaveLayerBounds) {
921 bounds = NULL;
922 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000923 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -0700924 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -0700925 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -0800926 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +0000927}
928
reed2ff1fce2014-12-11 07:07:37 -0800929void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -0700930 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000931#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000932 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000933#endif
934
junov@chromium.orga907ac32012-02-24 21:54:07 +0000935 // do this before we create the layer. We don't call the public save() since
936 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -0800937 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000938
939 fDeviceCMDirty = true;
940
941 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000942 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -0800943 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 }
945
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000946 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
947 // the clipRectBounds() call above?
948 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -0800949 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000950 }
951
reed76033be2015-03-14 10:54:31 -0700952 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -0700953 SkPixelGeometry geo = fProps.pixelGeometry();
954 if (paint) {
reed76033be2015-03-14 10:54:31 -0700955 // TODO: perhaps add a query to filters so we might preserve opaqueness...
956 if (paint->getImageFilter() || paint->getColorFilter()) {
957 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -0700958 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +0000959 }
960 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000961 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
962 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963
reedb2db8982014-11-13 12:41:02 -0800964 SkBaseDevice* device = this->getTopDevice();
965 if (NULL == device) {
966 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800967 return;
reed@google.com76dd2772012-01-05 21:15:07 +0000968 }
reedb2db8982014-11-13 12:41:02 -0800969
reed76033be2015-03-14 10:54:31 -0700970 SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed8dc0ccb2015-03-20 06:32:52 -0700971 device = device->onCreateDevice(SkBaseDevice::CreateInfo(info, usage, geo), paint);
bungeman@google.come25c6842011-08-17 14:53:54 +0000972 if (NULL == device) {
joshualitt5f5a8d72015-02-25 14:09:45 -0800973 SkErrorInternals::SetError( kInternalError_SkError,
974 "Unable to create device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800975 return;
bungeman@google.come25c6842011-08-17 14:53:54 +0000976 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000977
reed@google.com6f8f2922011-03-04 22:27:10 +0000978 device->setOrigin(ir.fLeft, ir.fTop);
reed96e657d2015-03-10 17:30:07 -0700979 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 device->unref();
981
982 layer->fNext = fMCRec->fTopLayer;
983 fMCRec->fLayer = layer;
984 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985}
986
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000987int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
988 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
989}
990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
992 SaveFlags flags) {
993 if (0xFF == alpha) {
994 return this->saveLayer(bounds, NULL, flags);
995 } else {
996 SkPaint tmpPaint;
997 tmpPaint.setAlpha(alpha);
998 return this->saveLayer(bounds, &tmpPaint, flags);
999 }
1000}
1001
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002void SkCanvas::internalRestore() {
1003 SkASSERT(fMCStack.count() != 0);
1004
1005 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001006 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007
reed687fa1c2015-04-07 08:00:56 -07001008 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001009
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001010 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 DeviceCM* layer = fMCRec->fLayer; // may be null
1012 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1013 fMCRec->fLayer = NULL;
1014
1015 // now do the normal restore()
1016 fMCRec->~MCRec(); // balanced in save()
1017 fMCStack.pop_back();
1018 fMCRec = (MCRec*)fMCStack.back();
1019
1020 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1021 since if we're being recorded, we don't want to record this (the
1022 recorder will have already recorded the restore).
1023 */
bsalomon49f085d2014-09-05 13:34:00 -07001024 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001026 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001027 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1028 layer->fPaint);
1029 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 fDeviceCMDirty = true;
reedb679ca82015-04-07 04:40:48 -07001031 SkDELETE(layer);
1032 } else {
1033 // we're at the root
1034 SkASSERT(layer == (void*)fBaseLayerStorage);
1035 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001037 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038}
1039
reed4a8126e2014-09-22 07:29:03 -07001040SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1041 if (NULL == props) {
1042 props = &fProps;
1043 }
1044 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001045}
1046
reed4a8126e2014-09-22 07:29:03 -07001047SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001048 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001049 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001050}
1051
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001052SkImageInfo SkCanvas::imageInfo() const {
1053 SkBaseDevice* dev = this->getDevice();
1054 if (dev) {
1055 return dev->imageInfo();
1056 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001057 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001058 }
1059}
1060
1061const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1062 return this->onPeekPixels(info, rowBytes);
1063}
1064
1065const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1066 SkBaseDevice* dev = this->getDevice();
1067 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1068}
1069
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001070void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1071 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1072 if (pixels && origin) {
1073 *origin = this->getTopDevice(false)->getOrigin();
1074 }
1075 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001076}
1077
1078void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1079 SkBaseDevice* dev = this->getTopDevice();
1080 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1081}
1082
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001083SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1084 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1085 if (NULL == fAddr) {
1086 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001087 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001088 return; // failure, fAddr is NULL
1089 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001090 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1091 return; // failure, fAddr is NULL
1092 }
1093 fAddr = fBitmap.getPixels();
1094 fRowBytes = fBitmap.rowBytes();
1095 }
1096 SkASSERT(fAddr); // success
1097}
1098
1099bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1100 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001101 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001102 } else {
1103 bitmap->reset();
1104 return false;
1105 }
1106}
1107
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001109void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001111 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 return;
1113 }
1114
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001115 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001117 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001119
1120 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001121
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001122 SkRect storage;
1123 const SkRect* bounds = NULL;
1124 if (paint && paint->canComputeFastBounds()) {
1125 bitmap.getBounds(&storage);
1126 matrix.mapRect(&storage);
1127 bounds = &paint->computeFastBounds(storage, &storage);
1128 }
1129
1130 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001131
1132 while (iter.next()) {
1133 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1134 }
1135
1136 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137}
1138
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001139void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001140 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 SkPaint tmp;
1142 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 paint = &tmp;
1144 }
reed@google.com4b226022011-01-11 18:32:13 +00001145
reed@google.com8926b162012-03-23 15:36:36 +00001146 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001148 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001149 paint = &looper.paint();
1150 SkImageFilter* filter = paint->getImageFilter();
1151 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001152 if (filter && !dstDev->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001153 SkDeviceImageFilterProxy proxy(dstDev, fProps);
reed@google.com76dd2772012-01-05 21:15:07 +00001154 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001155 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001156 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001157 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001158 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001159 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001160 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001161 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001162 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001163 SkPaint tmpUnfiltered(*paint);
1164 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001165 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1166 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001167 }
1168 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001169 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001170 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001172 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173}
1174
reed41af9662015-01-05 07:49:08 -08001175void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001176 if (gTreatSpriteAsBitmap) {
1177 this->save();
1178 this->resetMatrix();
1179 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1180 this->restore();
1181 return;
1182 }
1183
danakj9881d632014-11-26 12:41:06 -08001184 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001185 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001186 return;
1187 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001188 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001189
reed@google.com8926b162012-03-23 15:36:36 +00001190 SkPaint tmp;
1191 if (NULL == paint) {
1192 paint = &tmp;
1193 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001194
reed@google.com8926b162012-03-23 15:36:36 +00001195 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001196
reed@google.com8926b162012-03-23 15:36:36 +00001197 while (iter.next()) {
1198 paint = &looper.paint();
1199 SkImageFilter* filter = paint->getImageFilter();
1200 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1201 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001202 SkDeviceImageFilterProxy proxy(iter.fDevice, fProps);
reed@google.com8926b162012-03-23 15:36:36 +00001203 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001204 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001205 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001206 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001207 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001208 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001209 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001210 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001211 SkPaint tmpUnfiltered(*paint);
1212 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001213 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001214 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001215 }
1216 } else {
1217 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1218 }
1219 }
1220 LOOPER_END
1221}
1222
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001224void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001225 SkMatrix m;
1226 m.setTranslate(dx, dy);
1227 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228}
1229
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001230void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001231 SkMatrix m;
1232 m.setScale(sx, sy);
1233 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234}
1235
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001236void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001237 SkMatrix m;
1238 m.setRotate(degrees);
1239 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240}
1241
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001242void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001243 SkMatrix m;
1244 m.setSkew(sx, sy);
1245 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001246}
1247
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001248void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001249 if (matrix.isIdentity()) {
1250 return;
1251 }
1252
reed2ff1fce2014-12-11 07:07:37 -08001253 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001255 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001256 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001257
1258 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001259}
1260
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261void SkCanvas::setMatrix(const SkMatrix& matrix) {
reed2ff1fce2014-12-11 07:07:37 -08001262 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001264 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001265 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001266 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267}
1268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269void SkCanvas::resetMatrix() {
1270 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001271
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 matrix.reset();
1273 this->setMatrix(matrix);
1274}
1275
1276//////////////////////////////////////////////////////////////////////////////
1277
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001278void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001279 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001280 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1281 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001282}
1283
1284void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001285#ifdef SK_ENABLE_CLIP_QUICKREJECT
1286 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001287 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001288 return false;
1289 }
1290
reed@google.com3b3e8952012-08-16 20:53:31 +00001291 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001292 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001293 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001294
reed687fa1c2015-04-07 08:00:56 -07001295 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001296 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001297 }
1298 }
1299#endif
1300
reed@google.com5c3d1472011-02-22 19:12:23 +00001301 AutoValidateClip avc(this);
1302
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001304 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001305 if (!fAllowSoftClip) {
1306 edgeStyle = kHard_ClipEdgeStyle;
1307 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308
reed1f836ee2014-07-07 07:49:34 -07001309 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001310 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001311 // the matrix. This means we don't have to a) make a path, and b) tell
1312 // the region code to scan-convert the path, only to discover that it
1313 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315
reed1f836ee2014-07-07 07:49:34 -07001316 fMCRec->fMatrix.mapRect(&r, rect);
reed687fa1c2015-04-07 08:00:56 -07001317 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001318 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001320 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001321 // and clip against that, since it can handle any matrix. However, to
1322 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1323 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 SkPath path;
1325
1326 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001327 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 }
1329}
1330
reed73e714e2014-09-04 09:02:23 -07001331static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1332 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001333 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001334}
1335
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001336void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001337 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001338 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001339 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001340 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1341 } else {
1342 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001343 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001344}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001345
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001346void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001347 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001348 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001349 AutoValidateClip avc(this);
1350
1351 fDeviceCMDirty = true;
1352 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001353 if (!fAllowSoftClip) {
1354 edgeStyle = kHard_ClipEdgeStyle;
1355 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001356
reed687fa1c2015-04-07 08:00:56 -07001357 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001358
1359 SkPath devPath;
1360 devPath.addRRect(transformedRRect);
1361
reed73e714e2014-09-04 09:02:23 -07001362 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001363 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001364 }
1365
1366 SkPath path;
1367 path.addRRect(rrect);
1368 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001369 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001370}
1371
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001372void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001373 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001374 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1375 SkRect r;
1376 if (!path.isInverseFillType() && path.isRect(&r)) {
1377 this->onClipRect(r, op, edgeStyle);
1378 } else {
1379 this->onClipPath(path, op, edgeStyle);
1380 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001381}
1382
1383void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001384#ifdef SK_ENABLE_CLIP_QUICKREJECT
1385 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001386 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001387 return false;
1388 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001389
reed@google.com3b3e8952012-08-16 20:53:31 +00001390 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001391 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001392 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001393
reed687fa1c2015-04-07 08:00:56 -07001394 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001395 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001396 }
1397 }
1398#endif
1399
reed@google.com5c3d1472011-02-22 19:12:23 +00001400 AutoValidateClip avc(this);
1401
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001403 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001404 if (!fAllowSoftClip) {
1405 edgeStyle = kHard_ClipEdgeStyle;
1406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407
1408 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001409 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
reed@google.comfe701122011-11-08 19:41:23 +00001411 // Check if the transfomation, or the original path itself
1412 // made us empty. Note this can also happen if we contained NaN
1413 // values. computing the bounds detects this, and will set our
1414 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1415 if (devPath.getBounds().isEmpty()) {
1416 // resetting the path will remove any NaN or other wanky values
1417 // that might upset our scan converter.
1418 devPath.reset();
1419 }
1420
reed@google.com5c3d1472011-02-22 19:12:23 +00001421 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001422 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001423
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001424 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001425 bool clipIsAA = getClipStack()->asPath(&devPath);
1426 if (clipIsAA) {
1427 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001428 }
fmalita1a481fe2015-02-04 07:39:34 -08001429
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001430 op = SkRegion::kReplace_Op;
1431 }
1432
reed73e714e2014-09-04 09:02:23 -07001433 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434}
1435
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001436void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001437 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001438 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001439}
1440
1441void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001442 AutoValidateClip avc(this);
1443
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001445 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446
reed@google.com5c3d1472011-02-22 19:12:23 +00001447 // todo: signal fClipStack that we have a region, and therefore (I guess)
1448 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001449 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001450
reed1f836ee2014-07-07 07:49:34 -07001451 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452}
1453
reed@google.com819c9212011-02-23 18:56:55 +00001454#ifdef SK_DEBUG
1455void SkCanvas::validateClip() const {
1456 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001457 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001458 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001459 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001460 return;
1461 }
1462
reed@google.com819c9212011-02-23 18:56:55 +00001463 SkIRect ir;
1464 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001465 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001466
reed687fa1c2015-04-07 08:00:56 -07001467 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001468 const SkClipStack::Element* element;
1469 while ((element = iter.next()) != NULL) {
1470 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001471 case SkClipStack::Element::kRect_Type:
1472 element->getRect().round(&ir);
1473 tmpClip.op(ir, element->getOp());
1474 break;
1475 case SkClipStack::Element::kEmpty_Type:
1476 tmpClip.setEmpty();
1477 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001478 default: {
1479 SkPath path;
1480 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001481 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001482 break;
1483 }
reed@google.com819c9212011-02-23 18:56:55 +00001484 }
1485 }
reed@google.com819c9212011-02-23 18:56:55 +00001486}
1487#endif
1488
reed@google.com90c07ea2012-04-13 13:50:27 +00001489void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001490 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001491 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001492
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001493 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001494 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001495 }
1496}
1497
reed@google.com5c3d1472011-02-22 19:12:23 +00001498///////////////////////////////////////////////////////////////////////////////
1499
reed@google.com754de5f2014-02-24 19:38:20 +00001500bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001501 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001502}
1503
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001504bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001505 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001506}
1507
reed@google.com3b3e8952012-08-16 20:53:31 +00001508bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001509 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001510 return true;
1511
reed1f836ee2014-07-07 07:49:34 -07001512 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 return true;
1514 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515
reed1f836ee2014-07-07 07:49:34 -07001516 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001517 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001518 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001519 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001520 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001521 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001522
reed@android.coma380ae42009-07-21 01:17:02 +00001523 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001524 // TODO: should we use | instead, or compare all 4 at once?
1525 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001526 return true;
1527 }
reed@google.comc0784db2013-12-13 21:16:12 +00001528 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001529 return true;
1530 }
1531 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533}
1534
reed@google.com3b3e8952012-08-16 20:53:31 +00001535bool SkCanvas::quickReject(const SkPath& path) const {
1536 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537}
1538
reed@google.com3b3e8952012-08-16 20:53:31 +00001539bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001540 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001541 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 return false;
1543 }
1544
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001545 SkMatrix inverse;
1546 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001547 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001548 if (bounds) {
1549 bounds->setEmpty();
1550 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001551 return false;
1552 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553
bsalomon49f085d2014-09-05 13:34:00 -07001554 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001555 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001556 // adjust it outwards in case we are antialiasing
1557 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001558
reed@google.com8f4d2302013-12-17 16:44:46 +00001559 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1560 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561 inverse.mapRect(bounds, r);
1562 }
1563 return true;
1564}
1565
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001566bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001567 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001568 if (clip.isEmpty()) {
1569 if (bounds) {
1570 bounds->setEmpty();
1571 }
1572 return false;
1573 }
1574
bsalomon49f085d2014-09-05 13:34:00 -07001575 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001576 *bounds = clip.getBounds();
1577 }
1578 return true;
1579}
1580
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001582 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583}
1584
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001585const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001586 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001587}
1588
reed@google.com9c135db2014-03-12 18:28:35 +00001589GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1590 SkBaseDevice* dev = this->getTopDevice();
1591 return dev ? dev->accessRenderTarget() : NULL;
1592}
1593
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001594GrContext* SkCanvas::getGrContext() {
1595#if SK_SUPPORT_GPU
1596 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001597 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001598 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001599 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001600 return renderTarget->getContext();
1601 }
1602 }
1603#endif
1604
1605 return NULL;
1606
1607}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001608
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001609void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1610 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001611 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001612 if (outer.isEmpty()) {
1613 return;
1614 }
1615 if (inner.isEmpty()) {
1616 this->drawRRect(outer, paint);
1617 return;
1618 }
1619
1620 // We don't have this method (yet), but technically this is what we should
1621 // be able to assert...
1622 // SkASSERT(outer.contains(inner));
1623 //
1624 // For now at least check for containment of bounds
1625 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1626
1627 this->onDrawDRRect(outer, inner, paint);
1628}
1629
reed41af9662015-01-05 07:49:08 -08001630// These need to stop being virtual -- clients need to override the onDraw... versions
1631
1632void SkCanvas::drawPaint(const SkPaint& paint) {
1633 this->onDrawPaint(paint);
1634}
1635
1636void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1637 this->onDrawRect(r, paint);
1638}
1639
1640void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1641 this->onDrawOval(r, paint);
1642}
1643
1644void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1645 this->onDrawRRect(rrect, paint);
1646}
1647
1648void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1649 this->onDrawPoints(mode, count, pts, paint);
1650}
1651
1652void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1653 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1654 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1655 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1656 indices, indexCount, paint);
1657}
1658
1659void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1660 this->onDrawPath(path, paint);
1661}
1662
1663void SkCanvas::drawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
1664 this->onDrawImage(image, dx, dy, paint);
1665}
1666
1667void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1668 const SkPaint* paint) {
1669 this->onDrawImageRect(image, src, dst, paint);
1670}
1671
1672void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
tomhudson2df6fd62015-04-09 09:20:19 -07001673 if (bitmap.empty()) {
1674 return;
1675 }
reed41af9662015-01-05 07:49:08 -08001676 this->onDrawBitmap(bitmap, dx, dy, paint);
1677}
1678
1679void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1680 const SkPaint* paint, DrawBitmapRectFlags flags) {
tomhudson2df6fd62015-04-09 09:20:19 -07001681 if (bitmap.empty()) {
1682 return;
1683 }
reed41af9662015-01-05 07:49:08 -08001684 this->onDrawBitmapRect(bitmap, src, dst, paint, flags);
1685}
1686
1687void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1688 const SkPaint* paint) {
tomhudson2df6fd62015-04-09 09:20:19 -07001689 if (bitmap.empty()) {
1690 return;
1691 }
reed41af9662015-01-05 07:49:08 -08001692 this->onDrawBitmapNine(bitmap, center, dst, paint);
1693}
1694
1695void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
tomhudson2df6fd62015-04-09 09:20:19 -07001696 if (bitmap.empty()) {
1697 return;
1698 }
reed41af9662015-01-05 07:49:08 -08001699 this->onDrawSprite(bitmap, left, top, paint);
1700}
1701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702//////////////////////////////////////////////////////////////////////////////
1703// These are the virtual drawing methods
1704//////////////////////////////////////////////////////////////////////////////
1705
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001706void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001707 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001708 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1709 }
1710}
1711
reed41af9662015-01-05 07:49:08 -08001712void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001713 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001714 this->internalDrawPaint(paint);
1715}
1716
1717void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001718 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719
1720 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001721 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722 }
1723
reed@google.com4e2b3d32011-04-07 14:18:59 +00001724 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725}
1726
reed41af9662015-01-05 07:49:08 -08001727void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1728 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001729 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 if ((long)count <= 0) {
1731 return;
1732 }
1733
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001734 SkRect r, storage;
1735 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001736 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001737 // special-case 2 points (common for drawing a single line)
1738 if (2 == count) {
1739 r.set(pts[0], pts[1]);
1740 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001741 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001742 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001743 bounds = &paint.computeFastStrokeBounds(r, &storage);
1744 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001745 return;
1746 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001747 }
reed@google.coma584aed2012-05-16 14:06:02 +00001748
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749 SkASSERT(pts != NULL);
1750
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001751 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001752
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001754 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755 }
reed@google.com4b226022011-01-11 18:32:13 +00001756
reed@google.com4e2b3d32011-04-07 14:18:59 +00001757 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758}
1759
reed41af9662015-01-05 07:49:08 -08001760void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001761 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001762 SkRect storage;
1763 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08001765 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
1766 // To prevent accidental rejecting at this stage, we have to sort it before we check.
1767 SkRect tmp(r);
1768 tmp.sort();
1769
1770 bounds = &paint.computeFastBounds(tmp, &storage);
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001771 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 return;
1773 }
1774 }
reed@google.com4b226022011-01-11 18:32:13 +00001775
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001776 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777
1778 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001779 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780 }
1781
reed@google.com4e2b3d32011-04-07 14:18:59 +00001782 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783}
1784
reed41af9662015-01-05 07:49:08 -08001785void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001786 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001787 SkRect storage;
1788 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001789 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001790 bounds = &paint.computeFastBounds(oval, &storage);
1791 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001792 return;
1793 }
1794 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001795
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001797
1798 while (iter.next()) {
1799 iter.fDevice->drawOval(iter, oval, looper.paint());
1800 }
1801
1802 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001803}
1804
reed41af9662015-01-05 07:49:08 -08001805void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001806 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001807 SkRect storage;
1808 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001809 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001810 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1811 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001812 return;
1813 }
1814 }
1815
1816 if (rrect.isRect()) {
1817 // call the non-virtual version
1818 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001819 return;
1820 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001821 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001822 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1823 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001824 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001825
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001826 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001827
1828 while (iter.next()) {
1829 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1830 }
1831
1832 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001833}
1834
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001835void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1836 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001837 SkRect storage;
1838 const SkRect* bounds = NULL;
1839 if (paint.canComputeFastBounds()) {
1840 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1841 if (this->quickReject(*bounds)) {
1842 return;
1843 }
1844 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001845
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001846 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001847
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001848 while (iter.next()) {
1849 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1850 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001851
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001852 LOOPER_END
1853}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001854
reed41af9662015-01-05 07:49:08 -08001855void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001856 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001857 if (!path.isFinite()) {
1858 return;
1859 }
1860
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001861 SkRect storage;
1862 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001863 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001864 const SkRect& pathBounds = path.getBounds();
1865 bounds = &paint.computeFastBounds(pathBounds, &storage);
1866 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 return;
1868 }
1869 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001870
1871 const SkRect& r = path.getBounds();
1872 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001873 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001874 this->internalDrawPaint(paint);
1875 }
1876 return;
1877 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001879 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880
1881 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001882 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883 }
1884
reed@google.com4e2b3d32011-04-07 14:18:59 +00001885 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886}
1887
kkinnunena9baa652015-03-05 06:33:54 -08001888void SkCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001889 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
kkinnunena9baa652015-03-05 06:33:54 -08001890 image->draw(this, dx, dy, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001891}
1892
reed41af9662015-01-05 07:49:08 -08001893void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1894 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001895 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
kkinnunena9baa652015-03-05 06:33:54 -08001896 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001897}
1898
reed41af9662015-01-05 07:49:08 -08001899void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001900 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901 SkDEBUGCODE(bitmap.validate();)
1902
reed@google.com3d608122011-11-21 15:16:16 +00001903 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001904 SkRect bounds = {
1905 x, y,
1906 x + SkIntToScalar(bitmap.width()),
1907 y + SkIntToScalar(bitmap.height())
1908 };
1909 if (paint) {
1910 (void)paint->computeFastBounds(bounds, &bounds);
1911 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001912 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 return;
1914 }
1915 }
reed@google.com4b226022011-01-11 18:32:13 +00001916
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917 SkMatrix matrix;
1918 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001919 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920}
1921
reed@google.com9987ec32011-09-07 11:57:52 +00001922// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001923void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001924 const SkRect& dst, const SkPaint* paint,
1925 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001926 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 return;
1928 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001929
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001930 SkRect storage;
1931 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001932 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001933 if (paint) {
1934 bounds = &paint->computeFastBounds(dst, &storage);
1935 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001936 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001937 return;
1938 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001939 }
reed@google.com3d608122011-11-21 15:16:16 +00001940
reed@google.com33535f32012-09-25 15:37:50 +00001941 SkLazyPaint lazy;
1942 if (NULL == paint) {
1943 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001945
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001946 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001947
reed@google.com33535f32012-09-25 15:37:50 +00001948 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001949 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001950 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001951
reed@google.com33535f32012-09-25 15:37:50 +00001952 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953}
1954
reed41af9662015-01-05 07:49:08 -08001955void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1956 const SkPaint* paint, DrawBitmapRectFlags flags) {
danakj9881d632014-11-26 12:41:06 -08001957 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001958 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001959 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001960}
1961
reed@google.com9987ec32011-09-07 11:57:52 +00001962void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1963 const SkIRect& center, const SkRect& dst,
1964 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001965 if (bitmap.drawsNothing()) {
1966 return;
1967 }
reed@google.com3d608122011-11-21 15:16:16 +00001968 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001969 SkRect storage;
1970 const SkRect* bounds = &dst;
1971 if (paint) {
1972 bounds = &paint->computeFastBounds(dst, &storage);
1973 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001974 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001975 return;
1976 }
1977 }
1978
reed@google.com9987ec32011-09-07 11:57:52 +00001979 const int32_t w = bitmap.width();
1980 const int32_t h = bitmap.height();
1981
1982 SkIRect c = center;
1983 // pin center to the bounds of the bitmap
1984 c.fLeft = SkMax32(0, center.fLeft);
1985 c.fTop = SkMax32(0, center.fTop);
1986 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1987 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1988
reed@google.com71121732012-09-18 15:14:33 +00001989 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001990 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001991 };
1992 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001993 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001994 };
reed@google.com9987ec32011-09-07 11:57:52 +00001995 SkScalar dstX[4] = {
1996 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1997 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1998 };
1999 SkScalar dstY[4] = {
2000 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2001 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2002 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002003
reed@google.com9987ec32011-09-07 11:57:52 +00002004 if (dstX[1] > dstX[2]) {
2005 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2006 dstX[2] = dstX[1];
2007 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002008
reed@google.com9987ec32011-09-07 11:57:52 +00002009 if (dstY[1] > dstY[2]) {
2010 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2011 dstY[2] = dstY[1];
2012 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002013
reed@google.com9987ec32011-09-07 11:57:52 +00002014 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002015 SkRect s, d;
2016
reed@google.com9987ec32011-09-07 11:57:52 +00002017 s.fTop = srcY[y];
2018 s.fBottom = srcY[y+1];
2019 d.fTop = dstY[y];
2020 d.fBottom = dstY[y+1];
2021 for (int x = 0; x < 3; x++) {
2022 s.fLeft = srcX[x];
2023 s.fRight = srcX[x+1];
2024 d.fLeft = dstX[x];
2025 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002026 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002027 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002028 }
2029 }
2030}
2031
reed41af9662015-01-05 07:49:08 -08002032void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2033 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002034 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002035 SkDEBUGCODE(bitmap.validate();)
2036
2037 // Need a device entry-point, so gpu can use a mesh
2038 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2039}
2040
reed@google.comf67e4cf2011-03-15 20:56:58 +00002041class SkDeviceFilteredPaint {
2042public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002043 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002044 uint32_t filteredFlags = device->filterTextFlags(paint);
2045 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002046 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002047 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002048 fPaint = newPaint;
2049 } else {
2050 fPaint = &paint;
2051 }
2052 }
2053
reed@google.comf67e4cf2011-03-15 20:56:58 +00002054 const SkPaint& paint() const { return *fPaint; }
2055
2056private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002057 const SkPaint* fPaint;
2058 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002059};
2060
bungeman@google.com52c748b2011-08-22 21:30:43 +00002061void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2062 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002063 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002064 draw.fDevice->drawRect(draw, r, paint);
2065 } else {
2066 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002067 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002068 draw.fDevice->drawRect(draw, r, p);
2069 }
2070}
2071
2072void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2073 const char text[], size_t byteLength,
2074 SkScalar x, SkScalar y) {
2075 SkASSERT(byteLength == 0 || text != NULL);
2076
2077 // nothing to draw
2078 if (text == NULL || byteLength == 0 ||
2079 draw.fClip->isEmpty() ||
2080 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2081 return;
2082 }
2083
2084 SkScalar width = 0;
2085 SkPoint start;
2086
2087 start.set(0, 0); // to avoid warning
2088 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2089 SkPaint::kStrikeThruText_Flag)) {
2090 width = paint.measureText(text, byteLength);
2091
2092 SkScalar offsetX = 0;
2093 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2094 offsetX = SkScalarHalf(width);
2095 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2096 offsetX = width;
2097 }
2098 start.set(x - offsetX, y);
2099 }
2100
2101 if (0 == width) {
2102 return;
2103 }
2104
2105 uint32_t flags = paint.getFlags();
2106
2107 if (flags & (SkPaint::kUnderlineText_Flag |
2108 SkPaint::kStrikeThruText_Flag)) {
2109 SkScalar textSize = paint.getTextSize();
2110 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2111 SkRect r;
2112
2113 r.fLeft = start.fX;
2114 r.fRight = start.fX + width;
2115
2116 if (flags & SkPaint::kUnderlineText_Flag) {
2117 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2118 start.fY);
2119 r.fTop = offset;
2120 r.fBottom = offset + height;
2121 DrawRect(draw, paint, r, textSize);
2122 }
2123 if (flags & SkPaint::kStrikeThruText_Flag) {
2124 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2125 start.fY);
2126 r.fTop = offset;
2127 r.fBottom = offset + height;
2128 DrawRect(draw, paint, r, textSize);
2129 }
2130 }
2131}
2132
reed@google.come0d9ce82014-04-23 04:00:17 +00002133void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2134 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002135 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136
2137 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002138 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002139 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002140 DrawTextDecorations(iter, dfp.paint(),
2141 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 }
2143
reed@google.com4e2b3d32011-04-07 14:18:59 +00002144 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145}
2146
reed@google.come0d9ce82014-04-23 04:00:17 +00002147void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2148 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002149 SkPoint textOffset = SkPoint::Make(0, 0);
2150
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002151 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002152
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002154 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002155 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002156 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002158
reed@google.com4e2b3d32011-04-07 14:18:59 +00002159 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160}
2161
reed@google.come0d9ce82014-04-23 04:00:17 +00002162void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2163 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002164
2165 SkPoint textOffset = SkPoint::Make(0, constY);
2166
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002167 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002168
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002170 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002171 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002172 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002174
reed@google.com4e2b3d32011-04-07 14:18:59 +00002175 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176}
2177
reed@google.come0d9ce82014-04-23 04:00:17 +00002178void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2179 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002180 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002181
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182 while (iter.next()) {
2183 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002184 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002186
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002187 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002188}
2189
fmalita00d5c2c2014-08-21 08:53:26 -07002190void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2191 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002192
fmalita85d5eb92015-03-04 11:20:12 -08002193 SkRect storage;
2194 const SkRect* bounds = NULL;
fmalita19653d12014-10-16 11:53:30 -07002195 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002196 storage = blob->bounds().makeOffset(x, y);
2197 bounds = &paint.computeFastBounds(storage, &storage);
fmalita7ba7aa72014-08-29 09:46:36 -07002198
fmalita85d5eb92015-03-04 11:20:12 -08002199 if (this->quickReject(*bounds)) {
fmalita7ba7aa72014-08-29 09:46:36 -07002200 return;
2201 }
2202 }
2203
fmalita024f9962015-03-03 19:08:17 -08002204 // We cannot filter in the looper as we normally do, because the paint is
2205 // incomplete at this point (text-related attributes are embedded within blob run paints).
2206 SkDrawFilter* drawFilter = fMCRec->fFilter;
2207 fMCRec->fFilter = NULL;
2208
fmalita85d5eb92015-03-04 11:20:12 -08002209 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002210
fmalitaaa1b9122014-08-28 14:32:24 -07002211 while (iter.next()) {
2212 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002213 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002214 }
2215
fmalitaaa1b9122014-08-28 14:32:24 -07002216 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002217
2218 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002219}
2220
reed@google.come0d9ce82014-04-23 04:00:17 +00002221// These will become non-virtual, so they always call the (virtual) onDraw... method
2222void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2223 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002224 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002225 this->onDrawText(text, byteLength, x, y, paint);
2226}
2227void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2228 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002229 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002230 this->onDrawPosText(text, byteLength, pos, paint);
2231}
2232void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2233 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002234 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002235 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2236}
2237void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2238 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002239 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002240 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2241}
fmalita00d5c2c2014-08-21 08:53:26 -07002242void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2243 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002244 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002245 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002246 this->onDrawTextBlob(blob, x, y, paint);
2247 }
2248}
reed@google.come0d9ce82014-04-23 04:00:17 +00002249
reed41af9662015-01-05 07:49:08 -08002250void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2251 const SkPoint verts[], const SkPoint texs[],
2252 const SkColor colors[], SkXfermode* xmode,
2253 const uint16_t indices[], int indexCount,
2254 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002255 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002256 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002257
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 while (iter.next()) {
2259 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002260 colors, xmode, indices, indexCount,
2261 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262 }
reed@google.com4b226022011-01-11 18:32:13 +00002263
reed@google.com4e2b3d32011-04-07 14:18:59 +00002264 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265}
2266
dandovb3c9d1c2014-08-12 08:34:29 -07002267void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2268 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002269 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002270 if (NULL == cubics) {
2271 return;
2272 }
mtklein6cfa73a2014-08-13 13:33:49 -07002273
dandovecfff212014-08-04 10:02:00 -07002274 // Since a patch is always within the convex hull of the control points, we discard it when its
2275 // bounding rectangle is completely outside the current clip.
2276 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002277 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002278 if (this->quickReject(bounds)) {
2279 return;
2280 }
mtklein6cfa73a2014-08-13 13:33:49 -07002281
dandovb3c9d1c2014-08-12 08:34:29 -07002282 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2283}
2284
2285void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2286 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2287
dandovecfff212014-08-04 10:02:00 -07002288 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002289
dandovecfff212014-08-04 10:02:00 -07002290 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002291 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002292 }
mtklein6cfa73a2014-08-13 13:33:49 -07002293
dandovecfff212014-08-04 10:02:00 -07002294 LOOPER_END
2295}
2296
reed3cb38402015-02-06 08:36:15 -08002297void SkCanvas::drawDrawable(SkDrawable* dr) {
reed6be2aa92014-11-18 11:08:05 -08002298 if (dr && !this->quickReject(dr->getBounds())) {
2299 this->onDrawDrawable(dr);
reed6a070dc2014-11-11 19:36:09 -08002300 }
2301}
2302
reed3cb38402015-02-06 08:36:15 -08002303void SkCanvas::onDrawDrawable(SkDrawable* dr) {
reed6a070dc2014-11-11 19:36:09 -08002304 dr->draw(this);
2305}
2306
reed@android.com8a1c16f2008-12-17 15:59:43 +00002307//////////////////////////////////////////////////////////////////////////////
2308// These methods are NOT virtual, and therefore must call back into virtual
2309// methods, rather than actually drawing themselves.
2310//////////////////////////////////////////////////////////////////////////////
2311
2312void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002313 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002314 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002315 SkPaint paint;
2316
2317 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002318 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002319 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 }
2321 this->drawPaint(paint);
2322}
2323
reed@android.com845fdac2009-06-23 03:01:32 +00002324void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002325 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002326 SkPaint paint;
2327
2328 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002329 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002330 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002331 }
2332 this->drawPaint(paint);
2333}
2334
2335void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002336 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002337 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002338
reed@android.com8a1c16f2008-12-17 15:59:43 +00002339 pt.set(x, y);
2340 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2341}
2342
2343void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002344 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345 SkPoint pt;
2346 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002347
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348 pt.set(x, y);
2349 paint.setColor(color);
2350 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2351}
2352
2353void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2354 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002355 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002357
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358 pts[0].set(x0, y0);
2359 pts[1].set(x1, y1);
2360 this->drawPoints(kLines_PointMode, 2, pts, paint);
2361}
2362
2363void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2364 SkScalar right, SkScalar bottom,
2365 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002366 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367 SkRect r;
2368
2369 r.set(left, top, right, bottom);
2370 this->drawRect(r, paint);
2371}
2372
2373void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2374 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002375 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376 if (radius < 0) {
2377 radius = 0;
2378 }
2379
2380 SkRect r;
2381 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002382 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383}
2384
2385void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2386 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002387 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 if (rx > 0 && ry > 0) {
2389 if (paint.canComputeFastBounds()) {
2390 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002391 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392 return;
2393 }
2394 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002395 SkRRect rrect;
2396 rrect.setRectXY(r, rx, ry);
2397 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398 } else {
2399 this->drawRect(r, paint);
2400 }
2401}
2402
reed@android.com8a1c16f2008-12-17 15:59:43 +00002403void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2404 SkScalar sweepAngle, bool useCenter,
2405 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002406 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2408 this->drawOval(oval, paint);
2409 } else {
2410 SkPath path;
2411 if (useCenter) {
2412 path.moveTo(oval.centerX(), oval.centerY());
2413 }
2414 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2415 if (useCenter) {
2416 path.close();
2417 }
2418 this->drawPath(path, paint);
2419 }
2420}
2421
2422void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2423 const SkPath& path, SkScalar hOffset,
2424 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002425 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002427
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428 matrix.setTranslate(hOffset, vOffset);
2429 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2430}
2431
reed@android.comf76bacf2009-05-13 14:00:33 +00002432///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002433void SkCanvas::drawPicture(const SkPicture* picture) {
danakj9881d632014-11-26 12:41:06 -08002434 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002435 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002436 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002437 }
2438}
2439
reedd5fa1a42014-08-09 11:08:05 -07002440void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002441 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002442 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002443 if (matrix && matrix->isIdentity()) {
2444 matrix = NULL;
2445 }
2446 this->onDrawPicture(picture, matrix, paint);
2447 }
2448}
robertphillips9b14f262014-06-04 05:40:44 -07002449
reedd5fa1a42014-08-09 11:08:05 -07002450void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2451 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002452 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002453 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002454 // Canvas has to first give the device the opportunity to render
2455 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002456 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002457 return; // the device has rendered the entire picture
2458 }
2459 }
2460
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002461 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002462
robertphillipsc5ba71d2014-09-04 08:42:50 -07002463 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002464}
2465
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466///////////////////////////////////////////////////////////////////////////////
2467///////////////////////////////////////////////////////////////////////////////
2468
2469SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002470 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471
2472 SkASSERT(canvas);
2473
2474 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2475 fDone = !fImpl->next();
2476}
2477
2478SkCanvas::LayerIter::~LayerIter() {
2479 fImpl->~SkDrawIter();
2480}
2481
2482void SkCanvas::LayerIter::next() {
2483 fDone = !fImpl->next();
2484}
2485
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002486SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002487 return fImpl->getDevice();
2488}
2489
2490const SkMatrix& SkCanvas::LayerIter::matrix() const {
2491 return fImpl->getMatrix();
2492}
2493
2494const SkPaint& SkCanvas::LayerIter::paint() const {
2495 const SkPaint* paint = fImpl->getPaint();
2496 if (NULL == paint) {
2497 paint = &fDefaultPaint;
2498 }
2499 return *paint;
2500}
2501
2502const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2503int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2504int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002505
2506///////////////////////////////////////////////////////////////////////////////
2507
fmalitac3b589a2014-06-05 12:40:07 -07002508SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002509
2510///////////////////////////////////////////////////////////////////////////////
2511
2512static bool supported_for_raster_canvas(const SkImageInfo& info) {
2513 switch (info.alphaType()) {
2514 case kPremul_SkAlphaType:
2515 case kOpaque_SkAlphaType:
2516 break;
2517 default:
2518 return false;
2519 }
2520
2521 switch (info.colorType()) {
2522 case kAlpha_8_SkColorType:
2523 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002524 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002525 break;
2526 default:
2527 return false;
2528 }
2529
2530 return true;
2531}
2532
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002533SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2534 if (!supported_for_raster_canvas(info)) {
2535 return NULL;
2536 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002537
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002538 SkBitmap bitmap;
2539 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2540 return NULL;
2541 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002542 return SkNEW_ARGS(SkCanvas, (bitmap));
2543}
reedd5fa1a42014-08-09 11:08:05 -07002544
2545///////////////////////////////////////////////////////////////////////////////
2546
2547SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002548 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002549 : fCanvas(canvas)
2550 , fSaveCount(canvas->getSaveCount())
2551{
bsalomon49f085d2014-09-05 13:34:00 -07002552 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002553 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002554 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002555 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002556 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002557 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002558 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002559 canvas->save();
2560 }
mtklein6cfa73a2014-08-13 13:33:49 -07002561
bsalomon49f085d2014-09-05 13:34:00 -07002562 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002563 canvas->concat(*matrix);
2564 }
2565}
2566
2567SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2568 fCanvas->restoreToCount(fSaveCount);
2569}