blob: 13b84e7f0f38148f4a57e9d68da1f755ab8882c1 [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"
reed6a070dc2014-11-11 19:36:09 -08009#include "SkCanvasDrawable.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000012#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
piotaixrb5fae932014-09-24 13:03:30 -070016#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070019#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000021#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080022#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000023#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000024#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000025#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070027#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000028#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000029#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080030#include "SkTraceEvent.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000032
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000033#if SK_SUPPORT_GPU
34#include "GrRenderTarget.h"
35#endif
36
reedd990e2f2014-12-22 11:58:30 -080037static bool gIgnoreSaveLayerBounds;
38void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
39 gIgnoreSaveLayerBounds = ignore;
40}
41bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
42 return gIgnoreSaveLayerBounds;
43}
44
reed0acf1b42014-12-22 16:12:38 -080045static bool gTreatSpriteAsBitmap;
46void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
47 gTreatSpriteAsBitmap = spriteAsBitmap;
48}
49bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
50 return gTreatSpriteAsBitmap;
51}
52
reed@google.comda17f752012-08-16 18:27:05 +000053// experimental for faster tiled drawing...
54//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000055
reed@android.com8a1c16f2008-12-17 15:59:43 +000056//#define SK_TRACE_SAVERESTORE
57
58#ifdef SK_TRACE_SAVERESTORE
59 static int gLayerCounter;
60 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
61 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
62
63 static int gRecCounter;
64 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
65 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
66
67 static int gCanvasCounter;
68 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
69 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
70#else
71 #define inc_layer()
72 #define dec_layer()
73 #define inc_rec()
74 #define dec_rec()
75 #define inc_canvas()
76 #define dec_canvas()
77#endif
78
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000079typedef SkTLazy<SkPaint> SkLazyPaint;
80
reed@google.com97af1a62012-08-28 12:19:02 +000081void SkCanvas::predrawNotify() {
82 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000083 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000084 }
85}
86
reed@android.com8a1c16f2008-12-17 15:59:43 +000087///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000088
reed4a8126e2014-09-22 07:29:03 -070089static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
90 const uint32_t propFlags = props.flags();
91 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
92 flags &= ~SkPaint::kDither_Flag;
93 }
94 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
95 flags &= ~SkPaint::kAntiAlias_Flag;
96 }
97 return flags;
98}
99
100///////////////////////////////////////////////////////////////////////////////
101
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000102/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 The clip/matrix/proc are fields that reflect the top of the save/restore
104 stack. Whenever the canvas changes, it marks a dirty flag, and then before
105 these are used (assuming we're not on a layer) we rebuild these cache
106 values: they reflect the top of the save stack, but translated and clipped
107 by the device's XY offset and bitmap-bounds.
108*/
109struct DeviceCM {
110 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000111 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000112 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000114 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115
reedd9544982014-09-09 18:46:22 -0700116 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
117 bool conservativeRasterClip)
118 : fNext(NULL)
119 , fClip(conservativeRasterClip)
120 {
121 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000123 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 }
reed@google.com4b226022011-01-11 18:32:13 +0000125 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000127 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000129 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700130 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000131 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 fDevice->unref();
133 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000134 SkDELETE(fPaint);
135 }
reed@google.com4b226022011-01-11 18:32:13 +0000136
reed@google.com045e62d2011-10-24 12:19:46 +0000137 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
138 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000139 int x = fDevice->getOrigin().x();
140 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 int width = fDevice->width();
142 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000143
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 if ((x | y) == 0) {
145 fMatrix = &totalMatrix;
146 fClip = totalClip;
147 } else {
148 fMatrixStorage = totalMatrix;
149 fMatrixStorage.postTranslate(SkIntToScalar(-x),
150 SkIntToScalar(-y));
151 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000152
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 totalClip.translate(-x, -y, &fClip);
154 }
155
reed@google.com045e62d2011-10-24 12:19:46 +0000156 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157
158 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000161 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 SkRegion::kDifference_Op);
163 }
reed@google.com4b226022011-01-11 18:32:13 +0000164
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000165 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167#ifdef SK_DEBUG
168 if (!fClip.isEmpty()) {
169 SkIRect deviceR;
170 deviceR.set(0, 0, width, height);
171 SkASSERT(deviceR.contains(fClip.getBounds()));
172 }
173#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000174 }
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000177 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178};
179
180/* This is the record we keep for each save/restore level in the stack.
181 Since a level optionally copies the matrix and/or stack, we have pointers
182 for these fields. If the value is copied for this level, the copy is
183 stored in the ...Storage field, and the pointer points to that. If the
184 value is not copied for this level, we ignore ...Storage, and just point
185 at the corresponding value in the previous level in the stack.
186*/
187class SkCanvas::MCRec {
188public:
reed1f836ee2014-07-07 07:49:34 -0700189 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700190 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 /* If there are any layers in the stack, this points to the top-most
192 one that is at or below this level in the stack (so we know what
193 bitmap/device to draw into from this level. This value is NOT
194 reference counted, since the real owner is either our fLayer field,
195 or a previous one in a lower level.)
196 */
reed2ff1fce2014-12-11 07:07:37 -0800197 DeviceCM* fTopLayer;
198 SkRasterClip fRasterClip;
199 SkMatrix fMatrix;
200 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201
reedd9544982014-09-09 18:46:22 -0700202 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
reedd9544982014-09-09 18:46:22 -0700203 fFilter = NULL;
204 fLayer = NULL;
205 fTopLayer = NULL;
reed2ff1fce2014-12-11 07:07:37 -0800206 fMatrix.reset();
207 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700208
reedd9544982014-09-09 18:46:22 -0700209 // don't bother initializing fNext
210 inc_rec();
211 }
reed2ff1fce2014-12-11 07:07:37 -0800212 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700213 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700215 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800216 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700217
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 // don't bother initializing fNext
219 inc_rec();
220 }
221 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000222 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 SkDELETE(fLayer);
224 dec_rec();
225 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226};
227
228class SkDrawIter : public SkDraw {
229public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000230 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000231 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000232 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 canvas->updateDeviceCMCache();
234
reed@google.com90c07ea2012-04-13 13:50:27 +0000235 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000237 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 }
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 bool next() {
241 // skip over recs with empty clips
242 if (fSkipEmptyClips) {
243 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
244 fCurrLayer = fCurrLayer->fNext;
245 }
246 }
247
reed@google.comf68c5e22012-02-24 16:38:58 +0000248 const DeviceCM* rec = fCurrLayer;
249 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
251 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000252 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
253 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 fDevice = rec->fDevice;
255 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000257 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258
259 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000261
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 return true;
263 }
264 return false;
265 }
reed@google.com4b226022011-01-11 18:32:13 +0000266
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000267 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000268 int getX() const { return fDevice->getOrigin().x(); }
269 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 const SkMatrix& getMatrix() const { return *fMatrix; }
271 const SkRegion& getClip() const { return *fClip; }
272 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000273
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274private:
275 SkCanvas* fCanvas;
276 const DeviceCM* fCurrLayer;
277 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 SkBool8 fSkipEmptyClips;
279
280 typedef SkDraw INHERITED;
281};
282
283/////////////////////////////////////////////////////////////////////////////
284
285class AutoDrawLooper {
286public:
reed4a8126e2014-09-22 07:29:03 -0700287 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000288 bool skipLayerForImageFilter = false,
289 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000290 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700292 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000294 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000295 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296
reed@google.com8926b162012-03-23 15:36:36 +0000297 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
298 SkPaint tmp;
299 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000300 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
301 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000302 // we'll clear the imageFilter for the actual draws in next(), so
303 // it will only be applied during the restore().
304 fDoClearImageFilter = true;
305 }
306
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000307 if (SkDrawLooper* looper = paint.getLooper()) {
308 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
309 looper->contextSize());
310 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000311 fIsSimple = false;
312 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000313 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000314 // can we be marked as simple?
315 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 }
piotaixrb5fae932014-09-24 13:03:30 -0700317
reed4a8126e2014-09-22 07:29:03 -0700318 uint32_t oldFlags = paint.getFlags();
319 fNewPaintFlags = filter_paint_flags(props, oldFlags);
320 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
321 SkPaint* paint = fLazyPaint.set(fOrigPaint);
322 paint->setFlags(fNewPaintFlags);
323 fPaint = paint;
324 // if we're not simple, doNext() will take care of calling setFlags()
325 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000326 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000327
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000329 if (fDoClearImageFilter) {
330 fCanvas->internalRestore();
331 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000332 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000334
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 const SkPaint& paint() const {
336 SkASSERT(fPaint);
337 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000339
reed@google.com129ec222012-05-15 13:24:09 +0000340 bool next(SkDrawFilter::Type drawType) {
341 if (fDone) {
342 return false;
343 } else if (fIsSimple) {
344 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000345 return !fPaint->nothingToDraw();
346 } else {
347 return this->doNext(drawType);
348 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000349 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000350
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000352 SkLazyPaint fLazyPaint;
353 SkCanvas* fCanvas;
354 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000355 SkDrawFilter* fFilter;
356 const SkPaint* fPaint;
357 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700358 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000359 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000360 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000361 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000362 SkDrawLooper::Context* fLooperContext;
363 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000364
365 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366};
367
reed@google.com129ec222012-05-15 13:24:09 +0000368bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000369 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000370 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000371 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000372
373 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700374 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000375
376 if (fDoClearImageFilter) {
377 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000378 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000379
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000380 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000381 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000382 return false;
383 }
384 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000385 if (!fFilter->filter(paint, drawType)) {
386 fDone = true;
387 return false;
388 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000389 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000390 // no looper means we only draw once
391 fDone = true;
392 }
393 }
394 fPaint = paint;
395
396 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000397 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000398 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000399 }
400
401 // call this after any possible paint modifiers
402 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000403 fPaint = NULL;
404 return false;
405 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000406 return true;
407}
408
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409////////// macros to place around the internal draw calls //////////////////
410
reed@google.com8926b162012-03-23 15:36:36 +0000411#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000412 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700413 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000414 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000415 SkDrawIter iter(this);
416
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000417#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000418 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700419 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000420 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000422
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424
425////////////////////////////////////////////////////////////////////////////
426
reedd9544982014-09-09 18:46:22 -0700427SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
428 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000429 fCachedLocalClipBounds.setEmpty();
430 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000431 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000432 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700433 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800434 fSaveCount = 1;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000435 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436
437 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700438 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439
reedd9544982014-09-09 18:46:22 -0700440 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442
reed@google.com97af1a62012-08-28 12:19:02 +0000443 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000444
reedf92c8662014-08-18 08:02:43 -0700445 if (device) {
reedb2db8982014-11-13 12:41:02 -0800446 device->initForRootLayer(fProps.pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700447 if (device->forceConservativeRasterClip()) {
448 fConservativeRasterClip = true;
449 }
reedf92c8662014-08-18 08:02:43 -0700450 device->onAttachToCanvas(this);
451 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800452 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700453 }
454 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455}
456
reed@google.comcde92112011-07-06 20:00:52 +0000457SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000458 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700459 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000460{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000461 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000462
reedd9544982014-09-09 18:46:22 -0700463 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000464}
465
reedd9544982014-09-09 18:46:22 -0700466static SkBitmap make_nopixels(int width, int height) {
467 SkBitmap bitmap;
468 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
469 return bitmap;
470}
471
472class SkNoPixelsBitmapDevice : public SkBitmapDevice {
473public:
reed78e27682014-11-19 08:04:34 -0800474 SkNoPixelsBitmapDevice(const SkIRect& bounds)
475 : INHERITED(make_nopixels(bounds.width(), bounds.height()))
476 {
477 this->setOrigin(bounds.x(), bounds.y());
478 }
reedd9544982014-09-09 18:46:22 -0700479
480private:
piotaixrb5fae932014-09-24 13:03:30 -0700481
reedd9544982014-09-09 18:46:22 -0700482 typedef SkBitmapDevice INHERITED;
483};
484
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000485SkCanvas::SkCanvas(int width, int height)
486 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700487 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000488{
489 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700490
reed78e27682014-11-19 08:04:34 -0800491 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
492 (SkIRect::MakeWH(width, height))), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700493}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000494
reed78e27682014-11-19 08:04:34 -0800495SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700496 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700497 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700498{
499 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700500
reed78e27682014-11-19 08:04:34 -0800501 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700502}
503
reed4a8126e2014-09-22 07:29:03 -0700504SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700505 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700506 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700507{
508 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700509
reedd9544982014-09-09 18:46:22 -0700510 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000511}
512
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000513SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000514 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700515 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000516{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700518
reedd9544982014-09-09 18:46:22 -0700519 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520}
521
reed4a8126e2014-09-22 07:29:03 -0700522SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700523 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700524 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700525{
526 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700527
reed4a8126e2014-09-22 07:29:03 -0700528 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
529 this->init(device, kDefault_InitFlags);
530}
reed29c857d2014-09-21 10:25:07 -0700531
reed4a8126e2014-09-22 07:29:03 -0700532SkCanvas::SkCanvas(const SkBitmap& bitmap)
533 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
534 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
535{
536 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700537
reed4a8126e2014-09-22 07:29:03 -0700538 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
539 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540}
541
542SkCanvas::~SkCanvas() {
543 // free up the contents of our deque
544 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000545
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 this->internalRestore(); // restore the last, since we're going away
547
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000548 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000549
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 dec_canvas();
551}
552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553SkDrawFilter* SkCanvas::getDrawFilter() const {
554 return fMCRec->fFilter;
555}
556
557SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
558 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
559 return filter;
560}
561
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000562SkMetaData& SkCanvas::getMetaData() {
563 // metadata users are rare, so we lazily allocate it. If that changes we
564 // can decide to just make it a field in the device (rather than a ptr)
565 if (NULL == fMetaData) {
566 fMetaData = new SkMetaData;
567 }
568 return *fMetaData;
569}
570
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571///////////////////////////////////////////////////////////////////////////////
572
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000573void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000574 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000575 if (device) {
576 device->flush();
577 }
578}
579
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000580SkISize SkCanvas::getTopLayerSize() const {
581 SkBaseDevice* d = this->getTopDevice();
582 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
583}
584
585SkIPoint SkCanvas::getTopLayerOrigin() const {
586 SkBaseDevice* d = this->getTopDevice();
587 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
588}
589
590SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000591 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000592 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
593}
594
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000595SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000597 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 SkASSERT(rec && rec->fLayer);
599 return rec->fLayer->fDevice;
600}
601
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000602SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000603 if (updateMatrixClip) {
604 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
605 }
reed@google.com9266fed2011-03-30 00:18:03 +0000606 return fMCRec->fTopLayer->fDevice;
607}
608
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000609SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000611 SkDeque::F2BIter iter(fMCStack);
612 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000614 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615
616 if (rootDevice == device) {
617 return device;
618 }
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000621 device->onAttachToCanvas(this);
reedb2db8982014-11-13 12:41:02 -0800622 device->initForRootLayer(fProps.pixelGeometry());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 }
624 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000625 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 }
627
628 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
629 rootDevice = device;
630
631 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000632
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 /* Now we update our initial region to have the bounds of the new device,
634 and then intersect all of the clips in our stack with these bounds,
635 to ensure that we can't draw outside of the device's bounds (and trash
636 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000637
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 NOTE: this is only a partial-fix, since if the new device is larger than
639 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000640 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
642 reconstruct the correct clips, so this approximation will have to do.
643 The caller really needs to restore() back to the base if they want to
644 accurately take advantage of the new device bounds.
645 */
646
reed@google.com42aea282012-03-28 16:19:15 +0000647 SkIRect bounds;
648 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000650 } else {
651 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 }
reed@google.com42aea282012-03-28 16:19:15 +0000653 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700654 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000655 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700656 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000657 }
658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 return device;
660}
661
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000662bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
663 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
664 return false;
665 }
666
667 bool weAllocated = false;
668 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700669 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000670 return false;
671 }
672 weAllocated = true;
673 }
674
675 SkBitmap bm(*bitmap);
676 bm.lockPixels();
677 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
678 return true;
679 }
680
681 if (weAllocated) {
682 bitmap->setPixelRef(NULL);
683 }
684 return false;
685}
reed@google.com51df9e32010-12-23 19:29:18 +0000686
bsalomon@google.comc6980972011-11-02 19:57:21 +0000687bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000688 SkIRect r = srcRect;
689 const SkISize size = this->getBaseLayerSize();
690 if (!r.intersect(0, 0, size.width(), size.height())) {
691 bitmap->reset();
692 return false;
693 }
694
reed84825042014-09-02 12:50:45 -0700695 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000696 // bitmap will already be reset.
697 return false;
698 }
699 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
700 bitmap->reset();
701 return false;
702 }
703 return true;
704}
705
reed96472de2014-12-10 09:53:42 -0800706bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000707 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000708 if (!device) {
709 return false;
710 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000711 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800712
reed96472de2014-12-10 09:53:42 -0800713 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
714 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000715 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000716 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000717
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000718 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800719 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000720}
721
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000722bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
723 if (bitmap.getTexture()) {
724 return false;
725 }
726 SkBitmap bm(bitmap);
727 bm.lockPixels();
728 if (bm.getPixels()) {
729 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
730 }
731 return false;
732}
733
734bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
735 int x, int y) {
736 switch (origInfo.colorType()) {
737 case kUnknown_SkColorType:
738 case kIndex_8_SkColorType:
739 return false;
740 default:
741 break;
742 }
743 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
744 return false;
745 }
746
747 const SkISize size = this->getBaseLayerSize();
748 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
749 if (!target.intersect(0, 0, size.width(), size.height())) {
750 return false;
751 }
752
753 SkBaseDevice* device = this->getDevice();
754 if (!device) {
755 return false;
756 }
757
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000758 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700759 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000760
761 // if x or y are negative, then we have to adjust pixels
762 if (x > 0) {
763 x = 0;
764 }
765 if (y > 0) {
766 y = 0;
767 }
768 // here x,y are either 0 or negative
769 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
770
reed4af35f32014-06-27 17:47:49 -0700771 // Tell our owning surface to bump its generation ID
772 this->predrawNotify();
773
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000774 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000775 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000776}
reed@google.com51df9e32010-12-23 19:29:18 +0000777
junov@google.com4370aed2012-01-18 16:21:08 +0000778SkCanvas* SkCanvas::canvasForDrawIter() {
779 return this;
780}
781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782//////////////////////////////////////////////////////////////////////////////
783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784void SkCanvas::updateDeviceCMCache() {
785 if (fDeviceCMDirty) {
786 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700787 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000791 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000793 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000795 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 } while ((layer = layer->fNext) != NULL);
797 }
798 fDeviceCMDirty = false;
799 }
800}
801
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802///////////////////////////////////////////////////////////////////////////////
803
reed2ff1fce2014-12-11 07:07:37 -0800804void SkCanvas::checkForDeferredSave() {
805 if (fMCRec->fDeferredSaveCount > 0) {
806 fMCRec->fDeferredSaveCount -= 1;
807 this->doSave();
808 }
809}
810
reedf0090cb2014-11-26 08:55:51 -0800811int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800812#ifdef SK_DEBUG
813 int count = 0;
814 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
815 for (;;) {
816 const MCRec* rec = (const MCRec*)iter.next();
817 if (!rec) {
818 break;
819 }
820 count += 1 + rec->fDeferredSaveCount;
821 }
822 SkASSERT(count == fSaveCount);
823#endif
824 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800825}
826
827int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800828 fSaveCount += 1;
829 fMCRec->fDeferredSaveCount += 1;
830 return this->getSaveCount() - 1; // return our prev value
831}
832
833void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800834 this->willSave();
reed2ff1fce2014-12-11 07:07:37 -0800835 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800836}
837
838void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800839 if (fMCRec->fDeferredSaveCount > 0) {
840 SkASSERT(fSaveCount > 1);
841 fSaveCount -= 1;
842 fMCRec->fDeferredSaveCount -= 1;
843 } else {
844 // check for underflow
845 if (fMCStack.count() > 1) {
846 this->willRestore();
847 SkASSERT(fSaveCount > 1);
848 fSaveCount -= 1;
849 this->internalRestore();
850 this->didRestore();
851 }
reedf0090cb2014-11-26 08:55:51 -0800852 }
853}
854
855void SkCanvas::restoreToCount(int count) {
856 // sanity check
857 if (count < 1) {
858 count = 1;
859 }
mtkleinf0f14112014-12-12 08:46:25 -0800860
reedf0090cb2014-11-26 08:55:51 -0800861 int n = this->getSaveCount() - count;
862 for (int i = 0; i < n; ++i) {
863 this->restore();
864 }
865}
866
reed2ff1fce2014-12-11 07:07:37 -0800867void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700869 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000871
Florin Malita5f6102d2014-06-30 10:13:28 -0400872 fClipStack.save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873}
874
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000876#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000878#else
879 return true;
880#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881}
882
junov@chromium.orga907ac32012-02-24 21:54:07 +0000883bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000884 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000885 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000886 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000887 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000888 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000889 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000890
891 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700892 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000893 // Filters may grow the bounds beyond the device bounds.
894 op = SkRegion::kReplace_Op;
895 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000896 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700897 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000899
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 this->getTotalMatrix().mapRect(&r, *bounds);
901 r.roundOut(&ir);
902 // early exit if the layer's bounds are clipped out
903 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000904 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700905 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000906 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000907 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 }
909 } else { // no user bounds, so just use the clip
910 ir = clipBounds;
911 }
912
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000913 if (bounds_affects_clip(flags)) {
914 fClipStack.clipDevRect(ir, op);
915 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700916 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000917 return false;
918 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000919 }
920
921 if (intersection) {
922 *intersection = ir;
923 }
924 return true;
925}
926
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000927int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -0800928 if (gIgnoreSaveLayerBounds) {
929 bounds = NULL;
930 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000931 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reed2ff1fce2014-12-11 07:07:37 -0800932 fSaveCount += 1;
933 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
934 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000935}
936
reed2ff1fce2014-12-11 07:07:37 -0800937int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -0800938 if (gIgnoreSaveLayerBounds) {
939 bounds = NULL;
940 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000941 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reed2ff1fce2014-12-11 07:07:37 -0800942 fSaveCount += 1;
943 this->internalSaveLayer(bounds, paint, flags, false, strategy);
944 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +0000945}
946
reed2ff1fce2014-12-11 07:07:37 -0800947void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000948 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000949#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000950 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000951#endif
952
junov@chromium.orga907ac32012-02-24 21:54:07 +0000953 // do this before we create the layer. We don't call the public save() since
954 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -0800955 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000956
957 fDeviceCMDirty = true;
958
959 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000960 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -0800961 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 }
963
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000964 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
965 // the clipRectBounds() call above?
966 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -0800967 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000968 }
969
reed@google.comb55deeb2012-01-06 14:43:09 +0000970 // Kill the imagefilter if our device doesn't allow it
971 SkLazyPaint lazyP;
972 if (paint && paint->getImageFilter()) {
973 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000974 if (justForImageFilter) {
975 // early exit if the layer was just for the imageFilter
reed2ff1fce2014-12-11 07:07:37 -0800976 return;
reed@google.com8926b162012-03-23 15:36:36 +0000977 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000978 SkPaint* p = lazyP.set(*paint);
979 p->setImageFilter(NULL);
980 paint = p;
981 }
982 }
983
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000984 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
985 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
986 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987
reedb2db8982014-11-13 12:41:02 -0800988 SkBaseDevice* device = this->getTopDevice();
989 if (NULL == device) {
990 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800991 return;
reed@google.com76dd2772012-01-05 21:15:07 +0000992 }
reedb2db8982014-11-13 12:41:02 -0800993
994 SkBaseDevice::Usage usage = SkBaseDevice::kSaveLayer_Usage;
995 if (paint && paint->getImageFilter()) {
996 usage = SkBaseDevice::kImageFilter_Usage;
997 }
998 device = device->onCreateCompatibleDevice(SkBaseDevice::CreateInfo(info, usage,
999 fProps.pixelGeometry()));
bungeman@google.come25c6842011-08-17 14:53:54 +00001000 if (NULL == device) {
1001 SkDebugf("Unable to create device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001002 return;
bungeman@google.come25c6842011-08-17 14:53:54 +00001003 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001004
reed@google.com6f8f2922011-03-04 22:27:10 +00001005 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -07001006 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
1007 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 device->unref();
1009
1010 layer->fNext = fMCRec->fTopLayer;
1011 fMCRec->fLayer = layer;
1012 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013}
1014
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001015int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1016 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1017}
1018
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1020 SaveFlags flags) {
1021 if (0xFF == alpha) {
1022 return this->saveLayer(bounds, NULL, flags);
1023 } else {
1024 SkPaint tmpPaint;
1025 tmpPaint.setAlpha(alpha);
1026 return this->saveLayer(bounds, &tmpPaint, flags);
1027 }
1028}
1029
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030void SkCanvas::internalRestore() {
1031 SkASSERT(fMCStack.count() != 0);
1032
1033 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001034 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035
Florin Malita5f6102d2014-06-30 10:13:28 -04001036 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001037
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001038 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 DeviceCM* layer = fMCRec->fLayer; // may be null
1040 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1041 fMCRec->fLayer = NULL;
1042
1043 // now do the normal restore()
1044 fMCRec->~MCRec(); // balanced in save()
1045 fMCStack.pop_back();
1046 fMCRec = (MCRec*)fMCStack.back();
1047
1048 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1049 since if we're being recorded, we don't want to record this (the
1050 recorder will have already recorded the restore).
1051 */
bsalomon49f085d2014-09-05 13:34:00 -07001052 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001054 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001055 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1056 layer->fPaint);
1057 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 fDeviceCMDirty = true;
1059 }
1060 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001061 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062}
1063
reed4a8126e2014-09-22 07:29:03 -07001064SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1065 if (NULL == props) {
1066 props = &fProps;
1067 }
1068 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001069}
1070
reed4a8126e2014-09-22 07:29:03 -07001071SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001072 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001073 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001074}
1075
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001076SkImageInfo SkCanvas::imageInfo() const {
1077 SkBaseDevice* dev = this->getDevice();
1078 if (dev) {
1079 return dev->imageInfo();
1080 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001081 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001082 }
1083}
1084
1085const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1086 return this->onPeekPixels(info, rowBytes);
1087}
1088
1089const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1090 SkBaseDevice* dev = this->getDevice();
1091 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1092}
1093
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001094void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1095 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1096 if (pixels && origin) {
1097 *origin = this->getTopDevice(false)->getOrigin();
1098 }
1099 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001100}
1101
1102void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1103 SkBaseDevice* dev = this->getTopDevice();
1104 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1105}
1106
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001107SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1108 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1109 if (NULL == fAddr) {
1110 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001111 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001112 return; // failure, fAddr is NULL
1113 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001114 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1115 return; // failure, fAddr is NULL
1116 }
1117 fAddr = fBitmap.getPixels();
1118 fRowBytes = fBitmap.rowBytes();
1119 }
1120 SkASSERT(fAddr); // success
1121}
1122
1123bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1124 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001125 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001126 } else {
1127 bitmap->reset();
1128 return false;
1129 }
1130}
1131
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001133void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001135 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 return;
1137 }
1138
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001139 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001141 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001143
1144 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001145
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001146 SkRect storage;
1147 const SkRect* bounds = NULL;
1148 if (paint && paint->canComputeFastBounds()) {
1149 bitmap.getBounds(&storage);
1150 matrix.mapRect(&storage);
1151 bounds = &paint->computeFastBounds(storage, &storage);
1152 }
1153
1154 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001155
1156 while (iter.next()) {
1157 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1158 }
1159
1160 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161}
1162
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001163void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001164 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 SkPaint tmp;
1166 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 paint = &tmp;
1168 }
reed@google.com4b226022011-01-11 18:32:13 +00001169
reed@google.com8926b162012-03-23 15:36:36 +00001170 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001172 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001173 paint = &looper.paint();
1174 SkImageFilter* filter = paint->getImageFilter();
1175 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001176 if (filter && !dstDev->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001177 SkDeviceImageFilterProxy proxy(dstDev, fProps);
reed@google.com76dd2772012-01-05 21:15:07 +00001178 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001179 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001180 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001181 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001182 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001183 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001184 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001185 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001186 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001187 SkPaint tmpUnfiltered(*paint);
1188 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001189 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1190 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001191 }
1192 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001193 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001194 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001196 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
reed41af9662015-01-05 07:49:08 -08001199void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001200 if (gTreatSpriteAsBitmap) {
1201 this->save();
1202 this->resetMatrix();
1203 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1204 this->restore();
1205 return;
1206 }
1207
danakj9881d632014-11-26 12:41:06 -08001208 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001209 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001210 return;
1211 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001212 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001213
reed@google.com8926b162012-03-23 15:36:36 +00001214 SkPaint tmp;
1215 if (NULL == paint) {
1216 paint = &tmp;
1217 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001218
reed@google.com8926b162012-03-23 15:36:36 +00001219 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001220
reed@google.com8926b162012-03-23 15:36:36 +00001221 while (iter.next()) {
1222 paint = &looper.paint();
1223 SkImageFilter* filter = paint->getImageFilter();
1224 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1225 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001226 SkDeviceImageFilterProxy proxy(iter.fDevice, fProps);
reed@google.com8926b162012-03-23 15:36:36 +00001227 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001228 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001229 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001230 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001231 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001232 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001233 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001234 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001235 SkPaint tmpUnfiltered(*paint);
1236 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001237 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001238 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001239 }
1240 } else {
1241 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1242 }
1243 }
1244 LOOPER_END
1245}
1246
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001248void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001249 SkMatrix m;
1250 m.setTranslate(dx, dy);
1251 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252}
1253
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001254void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001255 SkMatrix m;
1256 m.setScale(sx, sy);
1257 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258}
1259
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001260void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001261 SkMatrix m;
1262 m.setRotate(degrees);
1263 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264}
1265
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001266void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001267 SkMatrix m;
1268 m.setSkew(sx, sy);
1269 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001270}
1271
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001272void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001273 if (matrix.isIdentity()) {
1274 return;
1275 }
1276
reed2ff1fce2014-12-11 07:07:37 -08001277 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001279 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001280 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001281
1282 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001283}
1284
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285void SkCanvas::setMatrix(const SkMatrix& matrix) {
reed2ff1fce2014-12-11 07:07:37 -08001286 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001288 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001289 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001290 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291}
1292
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293void SkCanvas::resetMatrix() {
1294 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001295
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 matrix.reset();
1297 this->setMatrix(matrix);
1298}
1299
1300//////////////////////////////////////////////////////////////////////////////
1301
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001302void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001303 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001304 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1305 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001306}
1307
1308void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001309#ifdef SK_ENABLE_CLIP_QUICKREJECT
1310 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001311 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001312 return false;
1313 }
1314
reed@google.com3b3e8952012-08-16 20:53:31 +00001315 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001316 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001317 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001318
1319 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001320 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001321 }
1322 }
1323#endif
1324
reed@google.com5c3d1472011-02-22 19:12:23 +00001325 AutoValidateClip avc(this);
1326
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001328 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001329 if (!fAllowSoftClip) {
1330 edgeStyle = kHard_ClipEdgeStyle;
1331 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332
reed1f836ee2014-07-07 07:49:34 -07001333 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001334 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001335 // the matrix. This means we don't have to a) make a path, and b) tell
1336 // the region code to scan-convert the path, only to discover that it
1337 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339
reed1f836ee2014-07-07 07:49:34 -07001340 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001341 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001342 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001344 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001345 // and clip against that, since it can handle any matrix. However, to
1346 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1347 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 SkPath path;
1349
1350 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001351 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 }
1353}
1354
reed73e714e2014-09-04 09:02:23 -07001355static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1356 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001357 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001358}
1359
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001360void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001361 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001362 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001363 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001364 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1365 } else {
1366 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001367 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001368}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001369
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001370void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001371 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001372 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001373 AutoValidateClip avc(this);
1374
1375 fDeviceCMDirty = true;
1376 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001377 if (!fAllowSoftClip) {
1378 edgeStyle = kHard_ClipEdgeStyle;
1379 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001380
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001381 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001382
1383 SkPath devPath;
1384 devPath.addRRect(transformedRRect);
1385
reed73e714e2014-09-04 09:02:23 -07001386 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001387 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001388 }
1389
1390 SkPath path;
1391 path.addRRect(rrect);
1392 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001393 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001394}
1395
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001396void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001397 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1399 SkRect r;
1400 if (!path.isInverseFillType() && path.isRect(&r)) {
1401 this->onClipRect(r, op, edgeStyle);
1402 } else {
1403 this->onClipPath(path, op, edgeStyle);
1404 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001405}
1406
1407void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001408#ifdef SK_ENABLE_CLIP_QUICKREJECT
1409 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001410 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001411 return false;
1412 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001413
reed@google.com3b3e8952012-08-16 20:53:31 +00001414 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001415 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001416 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001417
reed@google.comda17f752012-08-16 18:27:05 +00001418 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001419 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001420 }
1421 }
1422#endif
1423
reed@google.com5c3d1472011-02-22 19:12:23 +00001424 AutoValidateClip avc(this);
1425
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001427 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428 if (!fAllowSoftClip) {
1429 edgeStyle = kHard_ClipEdgeStyle;
1430 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431
1432 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001433 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434
reed@google.comfe701122011-11-08 19:41:23 +00001435 // Check if the transfomation, or the original path itself
1436 // made us empty. Note this can also happen if we contained NaN
1437 // values. computing the bounds detects this, and will set our
1438 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1439 if (devPath.getBounds().isEmpty()) {
1440 // resetting the path will remove any NaN or other wanky values
1441 // that might upset our scan converter.
1442 devPath.reset();
1443 }
1444
reed@google.com5c3d1472011-02-22 19:12:23 +00001445 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001446 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001447
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001448 if (fAllowSimplifyClip) {
1449 devPath.reset();
1450 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1451 const SkClipStack* clipStack = getClipStack();
1452 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1453 const SkClipStack::Element* element;
1454 while ((element = iter.next())) {
1455 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001456 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001457 if (type != SkClipStack::Element::kEmpty_Type) {
1458 element->asPath(&operand);
1459 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001460 SkRegion::Op elementOp = element->getOp();
1461 if (elementOp == SkRegion::kReplace_Op) {
1462 devPath = operand;
1463 } else {
1464 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1465 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001466 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1467 // perhaps we need an API change to avoid this sort of mixed-signals about
1468 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001469 if (element->isAA()) {
1470 edgeStyle = kSoft_ClipEdgeStyle;
1471 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001472 }
1473 op = SkRegion::kReplace_Op;
1474 }
1475
reed73e714e2014-09-04 09:02:23 -07001476 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001479void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001480 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001481 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001482}
1483
1484void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001485 AutoValidateClip avc(this);
1486
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001488 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489
reed@google.com5c3d1472011-02-22 19:12:23 +00001490 // todo: signal fClipStack that we have a region, and therefore (I guess)
1491 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001492 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001493
reed1f836ee2014-07-07 07:49:34 -07001494 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495}
1496
reed@google.com819c9212011-02-23 18:56:55 +00001497#ifdef SK_DEBUG
1498void SkCanvas::validateClip() const {
1499 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001500 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001501 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001502 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001503 return;
1504 }
1505
reed@google.com819c9212011-02-23 18:56:55 +00001506 SkIRect ir;
1507 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001508 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001509
robertphillips@google.com80214e22012-07-20 15:33:18 +00001510 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001511 const SkClipStack::Element* element;
1512 while ((element = iter.next()) != NULL) {
1513 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001514 case SkClipStack::Element::kRect_Type:
1515 element->getRect().round(&ir);
1516 tmpClip.op(ir, element->getOp());
1517 break;
1518 case SkClipStack::Element::kEmpty_Type:
1519 tmpClip.setEmpty();
1520 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001521 default: {
1522 SkPath path;
1523 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001524 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001525 break;
1526 }
reed@google.com819c9212011-02-23 18:56:55 +00001527 }
1528 }
reed@google.com819c9212011-02-23 18:56:55 +00001529}
1530#endif
1531
reed@google.com90c07ea2012-04-13 13:50:27 +00001532void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001533 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001534 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001535
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001536 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001537 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001538 }
1539}
1540
reed@google.com5c3d1472011-02-22 19:12:23 +00001541///////////////////////////////////////////////////////////////////////////////
1542
reed@google.com754de5f2014-02-24 19:38:20 +00001543bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001544 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001545}
1546
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001547bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001548 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001549}
1550
reed@google.com3b3e8952012-08-16 20:53:31 +00001551bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001552 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001553 return true;
1554
reed1f836ee2014-07-07 07:49:34 -07001555 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556 return true;
1557 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558
reed1f836ee2014-07-07 07:49:34 -07001559 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001560 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001561 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001562 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001563 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001564 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001565
reed@android.coma380ae42009-07-21 01:17:02 +00001566 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001567 // TODO: should we use | instead, or compare all 4 at once?
1568 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001569 return true;
1570 }
reed@google.comc0784db2013-12-13 21:16:12 +00001571 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001572 return true;
1573 }
1574 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576}
1577
reed@google.com3b3e8952012-08-16 20:53:31 +00001578bool SkCanvas::quickReject(const SkPath& path) const {
1579 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580}
1581
reed@google.com3b3e8952012-08-16 20:53:31 +00001582bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001583 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001584 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 return false;
1586 }
1587
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001588 SkMatrix inverse;
1589 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001590 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001591 if (bounds) {
1592 bounds->setEmpty();
1593 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001594 return false;
1595 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596
bsalomon49f085d2014-09-05 13:34:00 -07001597 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001598 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001599 // adjust it outwards in case we are antialiasing
1600 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001601
reed@google.com8f4d2302013-12-17 16:44:46 +00001602 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1603 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 inverse.mapRect(bounds, r);
1605 }
1606 return true;
1607}
1608
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001609bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001610 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001611 if (clip.isEmpty()) {
1612 if (bounds) {
1613 bounds->setEmpty();
1614 }
1615 return false;
1616 }
1617
bsalomon49f085d2014-09-05 13:34:00 -07001618 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001619 *bounds = clip.getBounds();
1620 }
1621 return true;
1622}
1623
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001625 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626}
1627
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001628const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001629 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001630}
1631
reed@google.com9c135db2014-03-12 18:28:35 +00001632GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1633 SkBaseDevice* dev = this->getTopDevice();
1634 return dev ? dev->accessRenderTarget() : NULL;
1635}
1636
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001637GrContext* SkCanvas::getGrContext() {
1638#if SK_SUPPORT_GPU
1639 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001640 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001641 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001642 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001643 return renderTarget->getContext();
1644 }
1645 }
1646#endif
1647
1648 return NULL;
1649
1650}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001651
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001652void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1653 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001654 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001655 if (outer.isEmpty()) {
1656 return;
1657 }
1658 if (inner.isEmpty()) {
1659 this->drawRRect(outer, paint);
1660 return;
1661 }
1662
1663 // We don't have this method (yet), but technically this is what we should
1664 // be able to assert...
1665 // SkASSERT(outer.contains(inner));
1666 //
1667 // For now at least check for containment of bounds
1668 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1669
1670 this->onDrawDRRect(outer, inner, paint);
1671}
1672
reed41af9662015-01-05 07:49:08 -08001673// These need to stop being virtual -- clients need to override the onDraw... versions
1674
1675void SkCanvas::drawPaint(const SkPaint& paint) {
1676 this->onDrawPaint(paint);
1677}
1678
1679void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1680 this->onDrawRect(r, paint);
1681}
1682
1683void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1684 this->onDrawOval(r, paint);
1685}
1686
1687void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1688 this->onDrawRRect(rrect, paint);
1689}
1690
1691void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1692 this->onDrawPoints(mode, count, pts, paint);
1693}
1694
1695void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1696 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1697 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1698 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1699 indices, indexCount, paint);
1700}
1701
1702void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1703 this->onDrawPath(path, paint);
1704}
1705
1706void SkCanvas::drawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
1707 this->onDrawImage(image, dx, dy, paint);
1708}
1709
1710void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1711 const SkPaint* paint) {
1712 this->onDrawImageRect(image, src, dst, paint);
1713}
1714
1715void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
1716 this->onDrawBitmap(bitmap, dx, dy, paint);
1717}
1718
1719void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1720 const SkPaint* paint, DrawBitmapRectFlags flags) {
1721 this->onDrawBitmapRect(bitmap, src, dst, paint, flags);
1722}
1723
1724void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1725 const SkPaint* paint) {
1726 this->onDrawBitmapNine(bitmap, center, dst, paint);
1727}
1728
1729void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
1730 this->onDrawSprite(bitmap, left, top, paint);
1731}
1732
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733//////////////////////////////////////////////////////////////////////////////
1734// These are the virtual drawing methods
1735//////////////////////////////////////////////////////////////////////////////
1736
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001737void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001738 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001739 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1740 }
1741}
1742
reed41af9662015-01-05 07:49:08 -08001743void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001744 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001745 this->internalDrawPaint(paint);
1746}
1747
1748void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001749 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750
1751 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001752 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753 }
1754
reed@google.com4e2b3d32011-04-07 14:18:59 +00001755 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756}
1757
reed41af9662015-01-05 07:49:08 -08001758void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1759 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001760 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 if ((long)count <= 0) {
1762 return;
1763 }
1764
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001765 SkRect r, storage;
1766 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001767 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001768 // special-case 2 points (common for drawing a single line)
1769 if (2 == count) {
1770 r.set(pts[0], pts[1]);
1771 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001772 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001773 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001774 bounds = &paint.computeFastStrokeBounds(r, &storage);
1775 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001776 return;
1777 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001778 }
reed@google.coma584aed2012-05-16 14:06:02 +00001779
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780 SkASSERT(pts != NULL);
1781
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001782 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001783
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001785 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 }
reed@google.com4b226022011-01-11 18:32:13 +00001787
reed@google.com4e2b3d32011-04-07 14:18:59 +00001788 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789}
1790
reed41af9662015-01-05 07:49:08 -08001791void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001792 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001793 SkRect storage;
1794 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 bounds = &paint.computeFastBounds(r, &storage);
1797 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 return;
1799 }
1800 }
reed@google.com4b226022011-01-11 18:32:13 +00001801
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001802 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803
1804 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806 }
1807
reed@google.com4e2b3d32011-04-07 14:18:59 +00001808 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809}
1810
reed41af9662015-01-05 07:49:08 -08001811void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001812 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001813 SkRect storage;
1814 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001815 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001816 bounds = &paint.computeFastBounds(oval, &storage);
1817 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001818 return;
1819 }
1820 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001821
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001822 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001823
1824 while (iter.next()) {
1825 iter.fDevice->drawOval(iter, oval, looper.paint());
1826 }
1827
1828 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001829}
1830
reed41af9662015-01-05 07:49:08 -08001831void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001832 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001833 SkRect storage;
1834 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001835 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001836 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1837 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001838 return;
1839 }
1840 }
1841
1842 if (rrect.isRect()) {
1843 // call the non-virtual version
1844 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001845 return;
1846 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001847 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001848 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1849 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001850 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001851
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001852 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001853
1854 while (iter.next()) {
1855 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1856 }
1857
1858 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001859}
1860
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001861void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1862 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001863 SkRect storage;
1864 const SkRect* bounds = NULL;
1865 if (paint.canComputeFastBounds()) {
1866 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1867 if (this->quickReject(*bounds)) {
1868 return;
1869 }
1870 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001871
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001872 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001873
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001874 while (iter.next()) {
1875 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1876 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001877
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001878 LOOPER_END
1879}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001880
reed41af9662015-01-05 07:49:08 -08001881void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001882 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001883 if (!path.isFinite()) {
1884 return;
1885 }
1886
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001887 SkRect storage;
1888 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001889 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001890 const SkRect& pathBounds = path.getBounds();
1891 bounds = &paint.computeFastBounds(pathBounds, &storage);
1892 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 return;
1894 }
1895 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001896
1897 const SkRect& r = path.getBounds();
1898 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001899 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001900 this->internalDrawPaint(paint);
1901 }
1902 return;
1903 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001905 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906
1907 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001908 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 }
1910
reed@google.com4e2b3d32011-04-07 14:18:59 +00001911 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912}
1913
reed41af9662015-01-05 07:49:08 -08001914void SkCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001915 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reed41af9662015-01-05 07:49:08 -08001916 image->draw(this, dx, dy, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001917}
1918
reed41af9662015-01-05 07:49:08 -08001919void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1920 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001921 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001922 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001923}
1924
reed41af9662015-01-05 07:49:08 -08001925void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001926 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 SkDEBUGCODE(bitmap.validate();)
1928
reed@google.com3d608122011-11-21 15:16:16 +00001929 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001930 SkRect bounds = {
1931 x, y,
1932 x + SkIntToScalar(bitmap.width()),
1933 y + SkIntToScalar(bitmap.height())
1934 };
1935 if (paint) {
1936 (void)paint->computeFastBounds(bounds, &bounds);
1937 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001938 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001939 return;
1940 }
1941 }
reed@google.com4b226022011-01-11 18:32:13 +00001942
reed@android.com8a1c16f2008-12-17 15:59:43 +00001943 SkMatrix matrix;
1944 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001945 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946}
1947
reed@google.com9987ec32011-09-07 11:57:52 +00001948// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001949void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001950 const SkRect& dst, const SkPaint* paint,
1951 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001952 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953 return;
1954 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001955
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001956 SkRect storage;
1957 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001958 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001959 if (paint) {
1960 bounds = &paint->computeFastBounds(dst, &storage);
1961 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001962 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001963 return;
1964 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965 }
reed@google.com3d608122011-11-21 15:16:16 +00001966
reed@google.com33535f32012-09-25 15:37:50 +00001967 SkLazyPaint lazy;
1968 if (NULL == paint) {
1969 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001970 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001971
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001972 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001973
reed@google.com33535f32012-09-25 15:37:50 +00001974 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001975 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001976 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001977
reed@google.com33535f32012-09-25 15:37:50 +00001978 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979}
1980
reed41af9662015-01-05 07:49:08 -08001981void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1982 const SkPaint* paint, DrawBitmapRectFlags flags) {
danakj9881d632014-11-26 12:41:06 -08001983 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001984 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001985 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001986}
1987
reed@google.com9987ec32011-09-07 11:57:52 +00001988void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1989 const SkIRect& center, const SkRect& dst,
1990 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001991 if (bitmap.drawsNothing()) {
1992 return;
1993 }
reed@google.com3d608122011-11-21 15:16:16 +00001994 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001995 SkRect storage;
1996 const SkRect* bounds = &dst;
1997 if (paint) {
1998 bounds = &paint->computeFastBounds(dst, &storage);
1999 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002000 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002001 return;
2002 }
2003 }
2004
reed@google.com9987ec32011-09-07 11:57:52 +00002005 const int32_t w = bitmap.width();
2006 const int32_t h = bitmap.height();
2007
2008 SkIRect c = center;
2009 // pin center to the bounds of the bitmap
2010 c.fLeft = SkMax32(0, center.fLeft);
2011 c.fTop = SkMax32(0, center.fTop);
2012 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2013 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2014
reed@google.com71121732012-09-18 15:14:33 +00002015 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002016 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002017 };
2018 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002019 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002020 };
reed@google.com9987ec32011-09-07 11:57:52 +00002021 SkScalar dstX[4] = {
2022 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2023 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2024 };
2025 SkScalar dstY[4] = {
2026 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2027 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2028 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002029
reed@google.com9987ec32011-09-07 11:57:52 +00002030 if (dstX[1] > dstX[2]) {
2031 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2032 dstX[2] = dstX[1];
2033 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002034
reed@google.com9987ec32011-09-07 11:57:52 +00002035 if (dstY[1] > dstY[2]) {
2036 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2037 dstY[2] = dstY[1];
2038 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002039
reed@google.com9987ec32011-09-07 11:57:52 +00002040 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002041 SkRect s, d;
2042
reed@google.com9987ec32011-09-07 11:57:52 +00002043 s.fTop = srcY[y];
2044 s.fBottom = srcY[y+1];
2045 d.fTop = dstY[y];
2046 d.fBottom = dstY[y+1];
2047 for (int x = 0; x < 3; x++) {
2048 s.fLeft = srcX[x];
2049 s.fRight = srcX[x+1];
2050 d.fLeft = dstX[x];
2051 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002052 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002053 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002054 }
2055 }
2056}
2057
reed41af9662015-01-05 07:49:08 -08002058void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2059 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002060 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002061 SkDEBUGCODE(bitmap.validate();)
2062
2063 // Need a device entry-point, so gpu can use a mesh
2064 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2065}
2066
reed@google.comf67e4cf2011-03-15 20:56:58 +00002067class SkDeviceFilteredPaint {
2068public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002069 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002070 uint32_t filteredFlags = device->filterTextFlags(paint);
2071 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002072 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002073 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002074 fPaint = newPaint;
2075 } else {
2076 fPaint = &paint;
2077 }
2078 }
2079
reed@google.comf67e4cf2011-03-15 20:56:58 +00002080 const SkPaint& paint() const { return *fPaint; }
2081
2082private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002083 const SkPaint* fPaint;
2084 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002085};
2086
bungeman@google.com52c748b2011-08-22 21:30:43 +00002087void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2088 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002089 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002090 draw.fDevice->drawRect(draw, r, paint);
2091 } else {
2092 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002093 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002094 draw.fDevice->drawRect(draw, r, p);
2095 }
2096}
2097
2098void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2099 const char text[], size_t byteLength,
2100 SkScalar x, SkScalar y) {
2101 SkASSERT(byteLength == 0 || text != NULL);
2102
2103 // nothing to draw
2104 if (text == NULL || byteLength == 0 ||
2105 draw.fClip->isEmpty() ||
2106 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2107 return;
2108 }
2109
2110 SkScalar width = 0;
2111 SkPoint start;
2112
2113 start.set(0, 0); // to avoid warning
2114 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2115 SkPaint::kStrikeThruText_Flag)) {
2116 width = paint.measureText(text, byteLength);
2117
2118 SkScalar offsetX = 0;
2119 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2120 offsetX = SkScalarHalf(width);
2121 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2122 offsetX = width;
2123 }
2124 start.set(x - offsetX, y);
2125 }
2126
2127 if (0 == width) {
2128 return;
2129 }
2130
2131 uint32_t flags = paint.getFlags();
2132
2133 if (flags & (SkPaint::kUnderlineText_Flag |
2134 SkPaint::kStrikeThruText_Flag)) {
2135 SkScalar textSize = paint.getTextSize();
2136 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2137 SkRect r;
2138
2139 r.fLeft = start.fX;
2140 r.fRight = start.fX + width;
2141
2142 if (flags & SkPaint::kUnderlineText_Flag) {
2143 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2144 start.fY);
2145 r.fTop = offset;
2146 r.fBottom = offset + height;
2147 DrawRect(draw, paint, r, textSize);
2148 }
2149 if (flags & SkPaint::kStrikeThruText_Flag) {
2150 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2151 start.fY);
2152 r.fTop = offset;
2153 r.fBottom = offset + height;
2154 DrawRect(draw, paint, r, textSize);
2155 }
2156 }
2157}
2158
reed@google.come0d9ce82014-04-23 04:00:17 +00002159void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2160 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002161 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162
2163 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002164 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002165 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002166 DrawTextDecorations(iter, dfp.paint(),
2167 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002168 }
2169
reed@google.com4e2b3d32011-04-07 14:18:59 +00002170 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171}
2172
reed@google.come0d9ce82014-04-23 04:00:17 +00002173void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2174 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002175 SkPoint textOffset = SkPoint::Make(0, 0);
2176
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002177 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002178
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002180 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002181 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002182 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002184
reed@google.com4e2b3d32011-04-07 14:18:59 +00002185 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186}
2187
reed@google.come0d9ce82014-04-23 04:00:17 +00002188void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2189 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002190
2191 SkPoint textOffset = SkPoint::Make(0, constY);
2192
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002193 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002194
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002196 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002197 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002198 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002200
reed@google.com4e2b3d32011-04-07 14:18:59 +00002201 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202}
2203
reed@google.come0d9ce82014-04-23 04:00:17 +00002204void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2205 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002206 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002207
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 while (iter.next()) {
2209 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002210 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002212
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002213 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002214}
2215
fmalita00d5c2c2014-08-21 08:53:26 -07002216void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2217 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002218
fmalita19653d12014-10-16 11:53:30 -07002219 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002220 SkRect storage;
2221
2222 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2223 return;
2224 }
2225 }
2226
fmalitaaa1b9122014-08-28 14:32:24 -07002227 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002228
fmalitaaa1b9122014-08-28 14:32:24 -07002229 while (iter.next()) {
2230 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2231 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002232 }
2233
fmalitaaa1b9122014-08-28 14:32:24 -07002234 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002235}
2236
reed@google.come0d9ce82014-04-23 04:00:17 +00002237// These will become non-virtual, so they always call the (virtual) onDraw... method
2238void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2239 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002240 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002241 this->onDrawText(text, byteLength, x, y, paint);
2242}
2243void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2244 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002245 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002246 this->onDrawPosText(text, byteLength, pos, paint);
2247}
2248void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2249 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002250 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002251 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2252}
2253void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2254 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002255 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002256 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2257}
fmalita00d5c2c2014-08-21 08:53:26 -07002258void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2259 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002260 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002261 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002262 this->onDrawTextBlob(blob, x, y, paint);
2263 }
2264}
reed@google.come0d9ce82014-04-23 04:00:17 +00002265
reed41af9662015-01-05 07:49:08 -08002266void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2267 const SkPoint verts[], const SkPoint texs[],
2268 const SkColor colors[], SkXfermode* xmode,
2269 const uint16_t indices[], int indexCount,
2270 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002271 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002272 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002273
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274 while (iter.next()) {
2275 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002276 colors, xmode, indices, indexCount,
2277 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278 }
reed@google.com4b226022011-01-11 18:32:13 +00002279
reed@google.com4e2b3d32011-04-07 14:18:59 +00002280 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281}
2282
dandovb3c9d1c2014-08-12 08:34:29 -07002283void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2284 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002285 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002286 if (NULL == cubics) {
2287 return;
2288 }
mtklein6cfa73a2014-08-13 13:33:49 -07002289
dandovecfff212014-08-04 10:02:00 -07002290 // Since a patch is always within the convex hull of the control points, we discard it when its
2291 // bounding rectangle is completely outside the current clip.
2292 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002293 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002294 if (this->quickReject(bounds)) {
2295 return;
2296 }
mtklein6cfa73a2014-08-13 13:33:49 -07002297
dandovb3c9d1c2014-08-12 08:34:29 -07002298 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2299}
2300
2301void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2302 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2303
dandovecfff212014-08-04 10:02:00 -07002304 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002305
dandovecfff212014-08-04 10:02:00 -07002306 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002307 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002308 }
mtklein6cfa73a2014-08-13 13:33:49 -07002309
dandovecfff212014-08-04 10:02:00 -07002310 LOOPER_END
2311}
2312
reed6a070dc2014-11-11 19:36:09 -08002313void SkCanvas::EXPERIMENTAL_drawDrawable(SkCanvasDrawable* dr) {
reed6be2aa92014-11-18 11:08:05 -08002314 if (dr && !this->quickReject(dr->getBounds())) {
2315 this->onDrawDrawable(dr);
reed6a070dc2014-11-11 19:36:09 -08002316 }
2317}
2318
2319void SkCanvas::onDrawDrawable(SkCanvasDrawable* dr) {
2320 dr->draw(this);
2321}
2322
reed@android.com8a1c16f2008-12-17 15:59:43 +00002323//////////////////////////////////////////////////////////////////////////////
2324// These methods are NOT virtual, and therefore must call back into virtual
2325// methods, rather than actually drawing themselves.
2326//////////////////////////////////////////////////////////////////////////////
2327
2328void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002329 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002330 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002331 SkPaint paint;
2332
2333 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002334 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002335 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 }
2337 this->drawPaint(paint);
2338}
2339
reed@android.com845fdac2009-06-23 03:01:32 +00002340void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002341 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002342 SkPaint paint;
2343
2344 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002345 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002346 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002347 }
2348 this->drawPaint(paint);
2349}
2350
2351void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002352 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002354
reed@android.com8a1c16f2008-12-17 15:59:43 +00002355 pt.set(x, y);
2356 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2357}
2358
2359void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002360 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002361 SkPoint pt;
2362 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002363
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364 pt.set(x, y);
2365 paint.setColor(color);
2366 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2367}
2368
2369void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2370 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002371 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002372 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002373
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 pts[0].set(x0, y0);
2375 pts[1].set(x1, y1);
2376 this->drawPoints(kLines_PointMode, 2, pts, paint);
2377}
2378
2379void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2380 SkScalar right, SkScalar bottom,
2381 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002382 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383 SkRect r;
2384
2385 r.set(left, top, right, bottom);
2386 this->drawRect(r, paint);
2387}
2388
2389void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2390 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002391 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392 if (radius < 0) {
2393 radius = 0;
2394 }
2395
2396 SkRect r;
2397 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002398 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399}
2400
2401void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2402 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002403 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002404 if (rx > 0 && ry > 0) {
2405 if (paint.canComputeFastBounds()) {
2406 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002407 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408 return;
2409 }
2410 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002411 SkRRect rrect;
2412 rrect.setRectXY(r, rx, ry);
2413 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002414 } else {
2415 this->drawRect(r, paint);
2416 }
2417}
2418
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2420 SkScalar sweepAngle, bool useCenter,
2421 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002422 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2424 this->drawOval(oval, paint);
2425 } else {
2426 SkPath path;
2427 if (useCenter) {
2428 path.moveTo(oval.centerX(), oval.centerY());
2429 }
2430 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2431 if (useCenter) {
2432 path.close();
2433 }
2434 this->drawPath(path, paint);
2435 }
2436}
2437
2438void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2439 const SkPath& path, SkScalar hOffset,
2440 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002441 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002443
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 matrix.setTranslate(hOffset, vOffset);
2445 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2446}
2447
reed@android.comf76bacf2009-05-13 14:00:33 +00002448///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002449void SkCanvas::drawPicture(const SkPicture* picture) {
danakj9881d632014-11-26 12:41:06 -08002450 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002451 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002452 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002453 }
2454}
2455
reedd5fa1a42014-08-09 11:08:05 -07002456void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002457 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002458 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002459 if (matrix && matrix->isIdentity()) {
2460 matrix = NULL;
2461 }
2462 this->onDrawPicture(picture, matrix, paint);
2463 }
2464}
robertphillips9b14f262014-06-04 05:40:44 -07002465
reedd5fa1a42014-08-09 11:08:05 -07002466void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2467 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002468 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002469 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002470 // Canvas has to first give the device the opportunity to render
2471 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002472 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002473 return; // the device has rendered the entire picture
2474 }
2475 }
2476
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002477 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002478
robertphillipsc5ba71d2014-09-04 08:42:50 -07002479 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002480}
2481
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482///////////////////////////////////////////////////////////////////////////////
2483///////////////////////////////////////////////////////////////////////////////
2484
2485SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002486 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002487
2488 SkASSERT(canvas);
2489
2490 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2491 fDone = !fImpl->next();
2492}
2493
2494SkCanvas::LayerIter::~LayerIter() {
2495 fImpl->~SkDrawIter();
2496}
2497
2498void SkCanvas::LayerIter::next() {
2499 fDone = !fImpl->next();
2500}
2501
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002502SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002503 return fImpl->getDevice();
2504}
2505
2506const SkMatrix& SkCanvas::LayerIter::matrix() const {
2507 return fImpl->getMatrix();
2508}
2509
2510const SkPaint& SkCanvas::LayerIter::paint() const {
2511 const SkPaint* paint = fImpl->getPaint();
2512 if (NULL == paint) {
2513 paint = &fDefaultPaint;
2514 }
2515 return *paint;
2516}
2517
2518const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2519int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2520int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002521
2522///////////////////////////////////////////////////////////////////////////////
2523
fmalitac3b589a2014-06-05 12:40:07 -07002524SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002525
2526///////////////////////////////////////////////////////////////////////////////
2527
2528static bool supported_for_raster_canvas(const SkImageInfo& info) {
2529 switch (info.alphaType()) {
2530 case kPremul_SkAlphaType:
2531 case kOpaque_SkAlphaType:
2532 break;
2533 default:
2534 return false;
2535 }
2536
2537 switch (info.colorType()) {
2538 case kAlpha_8_SkColorType:
2539 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002540 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002541 break;
2542 default:
2543 return false;
2544 }
2545
2546 return true;
2547}
2548
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002549SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2550 if (!supported_for_raster_canvas(info)) {
2551 return NULL;
2552 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002553
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002554 SkBitmap bitmap;
2555 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2556 return NULL;
2557 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002558 return SkNEW_ARGS(SkCanvas, (bitmap));
2559}
reedd5fa1a42014-08-09 11:08:05 -07002560
2561///////////////////////////////////////////////////////////////////////////////
2562
2563SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002564 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002565 : fCanvas(canvas)
2566 , fSaveCount(canvas->getSaveCount())
2567{
bsalomon49f085d2014-09-05 13:34:00 -07002568 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002569 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002570 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002571 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002572 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002573 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002574 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002575 canvas->save();
2576 }
mtklein6cfa73a2014-08-13 13:33:49 -07002577
bsalomon49f085d2014-09-05 13:34:00 -07002578 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002579 canvas->concat(*matrix);
2580 }
2581}
2582
2583SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2584 fCanvas->restoreToCount(fSaveCount);
2585}