blob: 587304bc353cbf89eaa2ac48d8d3d9834e5fca44 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
11#include "SkBounder.h"
12#include "SkDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000019#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000020#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000022#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000024#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000025#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027
reed@google.com82ce2b82012-06-26 17:43:26 +000028SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000029SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000030SK_DEFINE_INST_COUNT(SkDrawFilter)
31
reed@google.comda17f752012-08-16 18:27:05 +000032// experimental for faster tiled drawing...
33//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000034
reed@android.com8a1c16f2008-12-17 15:59:43 +000035//#define SK_TRACE_SAVERESTORE
36
37#ifdef SK_TRACE_SAVERESTORE
38 static int gLayerCounter;
39 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
40 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
41
42 static int gRecCounter;
43 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
44 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
45
46 static int gCanvasCounter;
47 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
48 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
49#else
50 #define inc_layer()
51 #define dec_layer()
52 #define inc_rec()
53 #define dec_rec()
54 #define inc_canvas()
55 #define dec_canvas()
56#endif
57
reed@google.comea033602012-12-14 13:13:55 +000058#ifdef SK_DEBUG
59#include "SkPixelRef.h"
60
61class AutoCheckLockCountBalance {
62public:
63 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
64 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
65 }
66 ~AutoCheckLockCountBalance() {
67 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
68 SkASSERT(count == fLockCount);
69 }
70
71private:
72 const SkPixelRef* fPixelRef;
73 int fLockCount;
74};
75
76class AutoCheckNoSetContext {
77public:
78 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
79 this->assertNoSetContext(fPaint);
80 }
81 ~AutoCheckNoSetContext() {
82 this->assertNoSetContext(fPaint);
83 }
84
85private:
86 const SkPaint& fPaint;
87
88 void assertNoSetContext(const SkPaint& paint) {
89 SkShader* s = paint.getShader();
90 if (s) {
91 SkASSERT(!s->setContextHasBeenCalled());
92 }
93 }
94};
95
96#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
97#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
98
99#else
100 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
101 #define CHECK_SHADER_NOSETCONTEXT(paint)
102#endif
103
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000104typedef SkTLazy<SkPaint> SkLazyPaint;
105
reed@google.com97af1a62012-08-28 12:19:02 +0000106void SkCanvas::predrawNotify() {
107 if (fSurfaceBase) {
108 fSurfaceBase->aboutToDraw(this);
109 }
110}
111
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113
114/* This is the record we keep for each SkDevice that the user installs.
115 The clip/matrix/proc are fields that reflect the top of the save/restore
116 stack. Whenever the canvas changes, it marks a dirty flag, and then before
117 these are used (assuming we're not on a layer) we rebuild these cache
118 values: they reflect the top of the save stack, but translated and clipped
119 by the device's XY offset and bitmap-bounds.
120*/
121struct DeviceCM {
122 DeviceCM* fNext;
123 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000124 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000126 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000128 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 : fNext(NULL) {
130 if (NULL != device) {
131 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000132 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 }
reed@google.com4b226022011-01-11 18:32:13 +0000134 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000136 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000138 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000140 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 fDevice->unref();
142 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000143 SkDELETE(fPaint);
144 }
reed@google.com4b226022011-01-11 18:32:13 +0000145
reed@google.com045e62d2011-10-24 12:19:46 +0000146 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
147 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000148 int x = fDevice->getOrigin().x();
149 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 int width = fDevice->width();
151 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000152
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 if ((x | y) == 0) {
154 fMatrix = &totalMatrix;
155 fClip = totalClip;
156 } else {
157 fMatrixStorage = totalMatrix;
158 fMatrixStorage.postTranslate(SkIntToScalar(-x),
159 SkIntToScalar(-y));
160 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000161
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 totalClip.translate(-x, -y, &fClip);
163 }
164
reed@google.com045e62d2011-10-24 12:19:46 +0000165 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166
167 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000168
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000170 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 SkRegion::kDifference_Op);
172 }
reed@google.com4b226022011-01-11 18:32:13 +0000173
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000174 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176#ifdef SK_DEBUG
177 if (!fClip.isEmpty()) {
178 SkIRect deviceR;
179 deviceR.set(0, 0, width, height);
180 SkASSERT(deviceR.contains(fClip.getBounds()));
181 }
182#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000183 }
184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000186 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187};
188
189/* This is the record we keep for each save/restore level in the stack.
190 Since a level optionally copies the matrix and/or stack, we have pointers
191 for these fields. If the value is copied for this level, the copy is
192 stored in the ...Storage field, and the pointer points to that. If the
193 value is not copied for this level, we ignore ...Storage, and just point
194 at the corresponding value in the previous level in the stack.
195*/
196class SkCanvas::MCRec {
197public:
198 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000199 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
200 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
201 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 DeviceCM* fLayer;
204 /* If there are any layers in the stack, this points to the top-most
205 one that is at or below this level in the stack (so we know what
206 bitmap/device to draw into from this level. This value is NOT
207 reference counted, since the real owner is either our fLayer field,
208 or a previous one in a lower level.)
209 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000210 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211
212 MCRec(const MCRec* prev, int flags) {
213 if (NULL != prev) {
214 if (flags & SkCanvas::kMatrix_SaveFlag) {
215 fMatrixStorage = *prev->fMatrix;
216 fMatrix = &fMatrixStorage;
217 } else {
218 fMatrix = prev->fMatrix;
219 }
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000222 fRasterClipStorage = *prev->fRasterClip;
223 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 } else {
reed@google.com00177082011-10-12 14:34:30 +0000225 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 }
227
228 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000229 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230
231 fTopLayer = prev->fTopLayer;
232 } else { // no prev
233 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000234
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000236 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 fFilter = NULL;
238 fTopLayer = NULL;
239 }
240 fLayer = NULL;
241
242 // don't bother initializing fNext
243 inc_rec();
244 }
245 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000246 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 SkDELETE(fLayer);
248 dec_rec();
249 }
reed@google.com4b226022011-01-11 18:32:13 +0000250
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251private:
reed@google.com00177082011-10-12 14:34:30 +0000252 SkMatrix fMatrixStorage;
253 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254};
255
256class SkDrawIter : public SkDraw {
257public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000258 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000259 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000260 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 canvas->updateDeviceCMCache();
262
reed@google.com90c07ea2012-04-13 13:50:27 +0000263 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 fBounder = canvas->getBounder();
265 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000266 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 bool next() {
270 // skip over recs with empty clips
271 if (fSkipEmptyClips) {
272 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
273 fCurrLayer = fCurrLayer->fNext;
274 }
275 }
276
reed@google.comf68c5e22012-02-24 16:38:58 +0000277 const DeviceCM* rec = fCurrLayer;
278 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279
280 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000281 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
282 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fDevice = rec->fDevice;
284 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000286 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287
288 fCurrLayer = rec->fNext;
289 if (fBounder) {
290 fBounder->setClip(fClip);
291 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000293
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 return true;
295 }
296 return false;
297 }
reed@google.com4b226022011-01-11 18:32:13 +0000298
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000300 int getX() const { return fDevice->getOrigin().x(); }
301 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 const SkMatrix& getMatrix() const { return *fMatrix; }
303 const SkRegion& getClip() const { return *fClip; }
304 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306private:
307 SkCanvas* fCanvas;
308 const DeviceCM* fCurrLayer;
309 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 SkBool8 fSkipEmptyClips;
311
312 typedef SkDraw INHERITED;
313};
314
315/////////////////////////////////////////////////////////////////////////////
316
317class AutoDrawLooper {
318public:
reed@google.com8926b162012-03-23 15:36:36 +0000319 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
320 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000321 fCanvas = canvas;
322 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000324 fPaint = NULL;
325 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000326 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000327 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328
reed@google.com8926b162012-03-23 15:36:36 +0000329 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
330 SkPaint tmp;
331 tmp.setImageFilter(fOrigPaint.getImageFilter());
332 // it would be nice if we had a guess at the bounds, instead of null
333 (void)canvas->internalSaveLayer(NULL, &tmp,
334 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
335 // we'll clear the imageFilter for the actual draws in next(), so
336 // it will only be applied during the restore().
337 fDoClearImageFilter = true;
338 }
339
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 if (fLooper) {
341 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000342 fIsSimple = false;
343 } else {
344 // can we be marked as simple?
345 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000346 }
347 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000348
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000350 if (fDoClearImageFilter) {
351 fCanvas->internalRestore();
352 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000353 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000355
reed@google.com4e2b3d32011-04-07 14:18:59 +0000356 const SkPaint& paint() const {
357 SkASSERT(fPaint);
358 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000360
reed@google.com129ec222012-05-15 13:24:09 +0000361 bool next(SkDrawFilter::Type drawType) {
362 if (fDone) {
363 return false;
364 } else if (fIsSimple) {
365 fDone = true;
366 fPaint = &fOrigPaint;
367 return !fPaint->nothingToDraw();
368 } else {
369 return this->doNext(drawType);
370 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000371 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000372
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000374 SkLazyPaint fLazyPaint;
375 SkCanvas* fCanvas;
376 const SkPaint& fOrigPaint;
377 SkDrawLooper* fLooper;
378 SkDrawFilter* fFilter;
379 const SkPaint* fPaint;
380 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000381 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000382 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000383 bool fIsSimple;
384
385 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386};
387
reed@google.com129ec222012-05-15 13:24:09 +0000388bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000389 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000390 SkASSERT(!fIsSimple);
391 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
392
393 SkPaint* paint = fLazyPaint.set(fOrigPaint);
394
395 if (fDoClearImageFilter) {
396 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000397 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000398
reed@google.com129ec222012-05-15 13:24:09 +0000399 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000400 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000401 return false;
402 }
403 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000404 if (!fFilter->filter(paint, drawType)) {
405 fDone = true;
406 return false;
407 }
reed@google.com129ec222012-05-15 13:24:09 +0000408 if (NULL == fLooper) {
409 // no looper means we only draw once
410 fDone = true;
411 }
412 }
413 fPaint = paint;
414
415 // if we only came in here for the imagefilter, mark us as done
416 if (!fLooper && !fFilter) {
417 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000418 }
419
420 // call this after any possible paint modifiers
421 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000422 fPaint = NULL;
423 return false;
424 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000425 return true;
426}
427
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428/* Stack helper for managing a SkBounder. In the destructor, if we were
429 given a bounder, we call its commit() method, signifying that we are
430 done accumulating bounds for that draw.
431*/
432class SkAutoBounderCommit {
433public:
434 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
435 ~SkAutoBounderCommit() {
436 if (NULL != fBounder) {
437 fBounder->commit();
438 }
439 }
440private:
441 SkBounder* fBounder;
442};
443
444#include "SkColorPriv.h"
445
446class AutoValidator {
447public:
448 AutoValidator(SkDevice* device) : fDevice(device) {}
449 ~AutoValidator() {
450#ifdef SK_DEBUG
451 const SkBitmap& bm = fDevice->accessBitmap(false);
452 if (bm.config() == SkBitmap::kARGB_4444_Config) {
453 for (int y = 0; y < bm.height(); y++) {
454 const SkPMColor16* p = bm.getAddr16(0, y);
455 for (int x = 0; x < bm.width(); x++) {
456 SkPMColor16 c = p[x];
457 SkPMColor16Assert(c);
458 }
459 }
460 }
461#endif
462 }
463private:
464 SkDevice* fDevice;
465};
466
467////////// macros to place around the internal draw calls //////////////////
468
reed@google.com8926b162012-03-23 15:36:36 +0000469#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
470/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000471 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000472 AutoDrawLooper looper(this, paint, true); \
473 while (looper.next(type)) { \
474 SkAutoBounderCommit ac(fBounder); \
475 SkDrawIter iter(this);
476
reed@google.com4e2b3d32011-04-07 14:18:59 +0000477#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000479 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000480 AutoDrawLooper looper(this, paint); \
481 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 SkAutoBounderCommit ac(fBounder); \
483 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000484
reed@google.com4e2b3d32011-04-07 14:18:59 +0000485#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486
487////////////////////////////////////////////////////////////////////////////
488
489SkDevice* SkCanvas::init(SkDevice* device) {
490 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000491 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000493 fAllowSoftClip = true;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000494 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000495 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000496 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497
498 fMCRec = (MCRec*)fMCStack.push_back();
499 new (fMCRec) MCRec(NULL, 0);
500
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000501 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502 fMCRec->fTopLayer = fMCRec->fLayer;
503 fMCRec->fNext = NULL;
504
reed@google.com97af1a62012-08-28 12:19:02 +0000505 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 return this->setDevice(device);
508}
509
reed@google.comcde92112011-07-06 20:00:52 +0000510SkCanvas::SkCanvas()
511: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000512 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000513
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000514 this->init(NULL);
515}
516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000518 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 inc_canvas();
520
521 this->init(device);
522}
523
524SkCanvas::SkCanvas(const SkBitmap& bitmap)
525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
526 inc_canvas();
527
reed@google.comcde92112011-07-06 20:00:52 +0000528 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529}
530
531SkCanvas::~SkCanvas() {
532 // free up the contents of our deque
533 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000534 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 this->internalRestore(); // restore the last, since we're going away
537
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000538 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000539 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000540
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 dec_canvas();
542}
543
544SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
545 SkRefCnt_SafeAssign(fBounder, bounder);
546 return bounder;
547}
548
549SkDrawFilter* SkCanvas::getDrawFilter() const {
550 return fMCRec->fFilter;
551}
552
553SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
554 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
555 return filter;
556}
557
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000558SkMetaData& SkCanvas::getMetaData() {
559 // metadata users are rare, so we lazily allocate it. If that changes we
560 // can decide to just make it a field in the device (rather than a ptr)
561 if (NULL == fMetaData) {
562 fMetaData = new SkMetaData;
563 }
564 return *fMetaData;
565}
566
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567///////////////////////////////////////////////////////////////////////////////
568
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000569void SkCanvas::flush() {
570 SkDevice* device = this->getDevice();
571 if (device) {
572 device->flush();
573 }
574}
575
reed@google.com210ce002011-11-01 14:24:23 +0000576SkISize SkCanvas::getDeviceSize() const {
577 SkDevice* d = this->getDevice();
578 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
579}
580
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581SkDevice* SkCanvas::getDevice() const {
582 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000583 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 SkASSERT(rec && rec->fLayer);
585 return rec->fLayer->fDevice;
586}
587
reed@google.com0b53d592012-03-19 18:26:34 +0000588SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
589 if (updateMatrixClip) {
590 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
591 }
reed@google.com9266fed2011-03-30 00:18:03 +0000592 return fMCRec->fTopLayer->fDevice;
593}
594
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595SkDevice* SkCanvas::setDevice(SkDevice* device) {
596 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000597 SkDeque::F2BIter iter(fMCStack);
598 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 SkASSERT(rec && rec->fLayer);
600 SkDevice* rootDevice = rec->fLayer->fDevice;
601
602 if (rootDevice == device) {
603 return device;
604 }
reed@google.com4b226022011-01-11 18:32:13 +0000605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000607 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 }
609 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000610 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 }
612
613 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
614 rootDevice = device;
615
616 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000617
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 /* Now we update our initial region to have the bounds of the new device,
619 and then intersect all of the clips in our stack with these bounds,
620 to ensure that we can't draw outside of the device's bounds (and trash
621 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 NOTE: this is only a partial-fix, since if the new device is larger than
624 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000625 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
627 reconstruct the correct clips, so this approximation will have to do.
628 The caller really needs to restore() back to the base if they want to
629 accurately take advantage of the new device bounds.
630 */
631
reed@google.com42aea282012-03-28 16:19:15 +0000632 SkIRect bounds;
633 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000635 } else {
636 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 }
reed@google.com42aea282012-03-28 16:19:15 +0000638 // now jam our 1st clip to be bounds, and intersect the rest with that
639 rec->fRasterClip->setRect(bounds);
640 while ((rec = (MCRec*)iter.next()) != NULL) {
641 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
642 }
643
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 return device;
645}
646
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000647bool SkCanvas::readPixels(SkBitmap* bitmap,
648 int x, int y,
649 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000650 SkDevice* device = this->getDevice();
651 if (!device) {
652 return false;
653 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000654 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000655}
656
bsalomon@google.comc6980972011-11-02 19:57:21 +0000657bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000658 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000659 if (!device) {
660 return false;
661 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000662
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000663 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000664 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000665 if (!bounds.intersect(srcRect)) {
666 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000667 }
668
669 SkBitmap tmp;
670 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
671 bounds.height());
672 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
673 bitmap->swap(tmp);
674 return true;
675 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000676 return false;
677 }
reed@google.com51df9e32010-12-23 19:29:18 +0000678}
679
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000680void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
681 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000682 SkDevice* device = this->getDevice();
683 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000684 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
685 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
686 device->accessBitmap(true);
687 device->writePixels(bitmap, x, y, config8888);
688 }
reed@google.com51df9e32010-12-23 19:29:18 +0000689 }
690}
691
junov@google.com4370aed2012-01-18 16:21:08 +0000692SkCanvas* SkCanvas::canvasForDrawIter() {
693 return this;
694}
695
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696//////////////////////////////////////////////////////////////////////////////
697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698void SkCanvas::updateDeviceCMCache() {
699 if (fDeviceCMDirty) {
700 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000701 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000703
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000705 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000707 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000709 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 } while ((layer = layer->fNext) != NULL);
711 }
712 fDeviceCMDirty = false;
713 }
714}
715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716///////////////////////////////////////////////////////////////////////////////
717
718int SkCanvas::internalSave(SaveFlags flags) {
719 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000720
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 MCRec* newTop = (MCRec*)fMCStack.push_back();
722 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000723
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 newTop->fNext = fMCRec;
725 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000726
reed@google.com5c3d1472011-02-22 19:12:23 +0000727 fClipStack.save();
728 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
729
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 return saveCount;
731}
732
733int SkCanvas::save(SaveFlags flags) {
734 // call shared impl
735 return this->internalSave(flags);
736}
737
738#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
739#define C16MASK (1 << SkBitmap::kRGB_565_Config)
740#define C8MASK (1 << SkBitmap::kA8_Config)
741
742static SkBitmap::Config resolve_config(SkCanvas* canvas,
743 const SkIRect& bounds,
744 SkCanvas::SaveFlags flags,
745 bool* isOpaque) {
746 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
747
748#if 0
749 // loop through and union all the configs we may draw into
750 uint32_t configMask = 0;
751 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
752 {
753 SkDevice* device = canvas->getLayerDevice(i);
754 if (device->intersects(bounds))
755 configMask |= 1 << device->config();
756 }
757
758 // if the caller wants alpha or fullcolor, we can't return 565
759 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
760 SkCanvas::kHasAlphaLayer_SaveFlag))
761 configMask &= ~C16MASK;
762
763 switch (configMask) {
764 case C8MASK: // if we only have A8, return that
765 return SkBitmap::kA8_Config;
766
767 case C16MASK: // if we only have 565, return that
768 return SkBitmap::kRGB_565_Config;
769
770 default:
771 return SkBitmap::kARGB_8888_Config; // default answer
772 }
773#else
774 return SkBitmap::kARGB_8888_Config; // default answer
775#endif
776}
777
778static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
779 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
780}
781
junov@chromium.orga907ac32012-02-24 21:54:07 +0000782bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
783 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000784 SkIRect clipBounds;
785 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000786 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000787 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000788 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 if (NULL != bounds) {
790 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 this->getTotalMatrix().mapRect(&r, *bounds);
793 r.roundOut(&ir);
794 // early exit if the layer's bounds are clipped out
795 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000796 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000797 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000798 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000799 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 }
801 } else { // no user bounds, so just use the clip
802 ir = clipBounds;
803 }
804
reed@google.com5c3d1472011-02-22 19:12:23 +0000805 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000806
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 // early exit if the clip is now empty
808 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000809 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 return false;
811 }
812
813 if (intersection) {
814 *intersection = ir;
815 }
816 return true;
817}
818
819int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
820 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000821 return this->internalSaveLayer(bounds, paint, flags, false);
822}
823
824int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
825 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000826 // do this before we create the layer. We don't call the public save() since
827 // that would invoke a possibly overridden virtual
828 int count = this->internalSave(flags);
829
830 fDeviceCMDirty = true;
831
832 SkIRect ir;
833 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 return count;
835 }
836
reed@google.comb55deeb2012-01-06 14:43:09 +0000837 // Kill the imagefilter if our device doesn't allow it
838 SkLazyPaint lazyP;
839 if (paint && paint->getImageFilter()) {
840 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000841 if (justForImageFilter) {
842 // early exit if the layer was just for the imageFilter
843 return count;
844 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000845 SkPaint* p = lazyP.set(*paint);
846 p->setImageFilter(NULL);
847 paint = p;
848 }
849 }
850
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 bool isOpaque;
852 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
853
reed@google.com76dd2772012-01-05 21:15:07 +0000854 SkDevice* device;
855 if (paint && paint->getImageFilter()) {
856 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
857 isOpaque);
858 } else {
859 device = this->createLayerDevice(config, ir.width(), ir.height(),
860 isOpaque);
861 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000862 if (NULL == device) {
863 SkDebugf("Unable to create device for layer.");
864 return count;
865 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000866
reed@google.com6f8f2922011-03-04 22:27:10 +0000867 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000868 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 device->unref();
870
871 layer->fNext = fMCRec->fTopLayer;
872 fMCRec->fLayer = layer;
873 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
874
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000875 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 return count;
877}
878
879int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
880 SaveFlags flags) {
881 if (0xFF == alpha) {
882 return this->saveLayer(bounds, NULL, flags);
883 } else {
884 SkPaint tmpPaint;
885 tmpPaint.setAlpha(alpha);
886 return this->saveLayer(bounds, &tmpPaint, flags);
887 }
888}
889
890void SkCanvas::restore() {
891 // check for underflow
892 if (fMCStack.count() > 1) {
893 this->internalRestore();
894 }
895}
896
897void SkCanvas::internalRestore() {
898 SkASSERT(fMCStack.count() != 0);
899
900 fDeviceCMDirty = true;
901 fLocalBoundsCompareTypeDirty = true;
902
reed@google.com5c3d1472011-02-22 19:12:23 +0000903 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000904 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 DeviceCM* layer = fMCRec->fLayer; // may be null
906 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
907 fMCRec->fLayer = NULL;
908
909 // now do the normal restore()
910 fMCRec->~MCRec(); // balanced in save()
911 fMCStack.pop_back();
912 fMCRec = (MCRec*)fMCStack.back();
913
914 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
915 since if we're being recorded, we don't want to record this (the
916 recorder will have already recorded the restore).
917 */
918 if (NULL != layer) {
919 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000920 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000921 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
922 layer->fPaint);
923 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000925
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000926 SkASSERT(fSaveLayerCount > 0);
927 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 }
929 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000930 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000931
932 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933}
934
935int SkCanvas::getSaveCount() const {
936 return fMCStack.count();
937}
938
939void SkCanvas::restoreToCount(int count) {
940 // sanity check
941 if (count < 1) {
942 count = 1;
943 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000944
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000945 int n = this->getSaveCount() - count;
946 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 this->restore();
948 }
949}
950
reed@google.com7c202932011-12-14 18:48:05 +0000951bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000952 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000953}
954
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955/////////////////////////////////////////////////////////////////////////////
956
957// can't draw it if its empty, or its too big for a fixed-point width or height
958static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000959 return bitmap.width() <= 0 || bitmap.height() <= 0
960#ifndef SK_ALLOW_OVER_32K_BITMAPS
961 || bitmap.width() > 32767 || bitmap.height() > 32767
962#endif
963 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964}
965
reed@android.comf2b98d62010-12-20 18:26:13 +0000966void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 const SkMatrix& matrix, const SkPaint* paint) {
968 if (reject_bitmap(bitmap)) {
969 return;
970 }
971
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000972 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000974 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000976 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977}
978
reed@google.com8926b162012-03-23 15:36:36 +0000979void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
980 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 SkPaint tmp;
982 if (NULL == paint) {
983 tmp.setDither(true);
984 paint = &tmp;
985 }
reed@google.com4b226022011-01-11 18:32:13 +0000986
reed@google.com8926b162012-03-23 15:36:36 +0000987 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000989 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000990 paint = &looper.paint();
991 SkImageFilter* filter = paint->getImageFilter();
992 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000993 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000994 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000995 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000996 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000997 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000998 SkPaint tmpUnfiltered(*paint);
999 tmpUnfiltered.setImageFilter(NULL);
1000 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001001 }
1002 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001003 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001004 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001006 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007}
1008
reed@google.com8926b162012-03-23 15:36:36 +00001009void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1010 const SkPaint* paint) {
1011 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001012 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001013
reed@google.com8926b162012-03-23 15:36:36 +00001014 if (reject_bitmap(bitmap)) {
1015 return;
1016 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001017
reed@google.com8926b162012-03-23 15:36:36 +00001018 SkPaint tmp;
1019 if (NULL == paint) {
1020 paint = &tmp;
1021 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001022
reed@google.com8926b162012-03-23 15:36:36 +00001023 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001024
reed@google.com8926b162012-03-23 15:36:36 +00001025 while (iter.next()) {
1026 paint = &looper.paint();
1027 SkImageFilter* filter = paint->getImageFilter();
1028 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1029 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001030 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001031 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001032 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1033 &dst, &pos)) {
1034 SkPaint tmpUnfiltered(*paint);
1035 tmpUnfiltered.setImageFilter(NULL);
1036 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1037 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001038 }
1039 } else {
1040 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1041 }
1042 }
1043 LOOPER_END
1044}
1045
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046/////////////////////////////////////////////////////////////////////////////
1047
1048bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1049 fDeviceCMDirty = true;
1050 fLocalBoundsCompareTypeDirty = true;
1051 return fMCRec->fMatrix->preTranslate(dx, dy);
1052}
1053
1054bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1055 fDeviceCMDirty = true;
1056 fLocalBoundsCompareTypeDirty = true;
1057 return fMCRec->fMatrix->preScale(sx, sy);
1058}
1059
1060bool SkCanvas::rotate(SkScalar degrees) {
1061 fDeviceCMDirty = true;
1062 fLocalBoundsCompareTypeDirty = true;
1063 return fMCRec->fMatrix->preRotate(degrees);
1064}
1065
1066bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1067 fDeviceCMDirty = true;
1068 fLocalBoundsCompareTypeDirty = true;
1069 return fMCRec->fMatrix->preSkew(sx, sy);
1070}
1071
1072bool SkCanvas::concat(const SkMatrix& matrix) {
1073 fDeviceCMDirty = true;
1074 fLocalBoundsCompareTypeDirty = true;
1075 return fMCRec->fMatrix->preConcat(matrix);
1076}
1077
1078void SkCanvas::setMatrix(const SkMatrix& matrix) {
1079 fDeviceCMDirty = true;
1080 fLocalBoundsCompareTypeDirty = true;
1081 *fMCRec->fMatrix = matrix;
1082}
1083
1084// this is not virtual, so it must call a virtual method so that subclasses
1085// will see its action
1086void SkCanvas::resetMatrix() {
1087 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001088
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 matrix.reset();
1090 this->setMatrix(matrix);
1091}
1092
1093//////////////////////////////////////////////////////////////////////////////
1094
reed@google.comc42d35d2011-10-12 11:57:42 +00001095bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001096#ifdef SK_ENABLE_CLIP_QUICKREJECT
1097 if (SkRegion::kIntersect_Op == op) {
1098 if (fMCRec->fRasterClip->isEmpty()) {
1099 return false;
1100 }
1101
reed@google.com3b3e8952012-08-16 20:53:31 +00001102 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001103 fDeviceCMDirty = true;
1104 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001105
1106 fClipStack.clipEmpty();
1107 return fMCRec->fRasterClip->setEmpty();
1108 }
1109 }
1110#endif
1111
reed@google.com5c3d1472011-02-22 19:12:23 +00001112 AutoValidateClip avc(this);
1113
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 fDeviceCMDirty = true;
1115 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001116 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117
1118 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001119 // for these simpler matrices, we can stay a rect ever after applying
1120 // the matrix. This means we don't have to a) make a path, and b) tell
1121 // the region code to scan-convert the path, only to discover that it
1122 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124
1125 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001126 fClipStack.clipDevRect(r, op, doAA);
1127 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001129 // since we're rotate or some such thing, we convert the rect to a path
1130 // and clip against that, since it can handle any matrix. However, to
1131 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1132 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 SkPath path;
1134
1135 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001136 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 }
1138}
1139
reed@google.com00177082011-10-12 14:34:30 +00001140static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001141 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001142 // base is used to limit the size (and therefore memory allocation) of the
1143 // region that results from scan converting devPath.
1144 SkRegion base;
1145
reed@google.com819c9212011-02-23 18:56:55 +00001146 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001147 // since we are intersect, we can do better (tighter) with currRgn's
1148 // bounds, than just using the device. However, if currRgn is complex,
1149 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001150 if (currClip->isRect()) {
1151 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001152 } else {
reed@google.com00177082011-10-12 14:34:30 +00001153 base.setRect(currClip->getBounds());
1154 SkRasterClip clip;
1155 clip.setPath(devPath, base, doAA);
1156 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001157 }
reed@google.com819c9212011-02-23 18:56:55 +00001158 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001159 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001160 if (!device) {
1161 return currClip->setEmpty();
1162 }
1163
junov@chromium.orga907ac32012-02-24 21:54:07 +00001164 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001165
1166 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001167 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001168 } else {
reed@google.com00177082011-10-12 14:34:30 +00001169 SkRasterClip clip;
1170 clip.setPath(devPath, base, doAA);
1171 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001172 }
1173 }
1174}
1175
reed@google.com4ed0fb72012-12-12 20:48:18 +00001176bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1177 if (rrect.isRect()) {
1178 // call the non-virtual version
1179 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1180 } else {
1181 SkPath path;
1182 path.addRRect(rrect);
1183 // call the non-virtual version
1184 return this->SkCanvas::clipPath(path, op, doAA);
1185 }
1186}
1187
reed@google.comc42d35d2011-10-12 11:57:42 +00001188bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001189#ifdef SK_ENABLE_CLIP_QUICKREJECT
1190 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1191 if (fMCRec->fRasterClip->isEmpty()) {
1192 return false;
1193 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001194
reed@google.com3b3e8952012-08-16 20:53:31 +00001195 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001196 fDeviceCMDirty = true;
1197 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001198
reed@google.comda17f752012-08-16 18:27:05 +00001199 fClipStack.clipEmpty();
1200 return fMCRec->fRasterClip->setEmpty();
1201 }
1202 }
1203#endif
1204
reed@google.com5c3d1472011-02-22 19:12:23 +00001205 AutoValidateClip avc(this);
1206
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 fDeviceCMDirty = true;
1208 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001209 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210
1211 SkPath devPath;
1212 path.transform(*fMCRec->fMatrix, &devPath);
1213
reed@google.comfe701122011-11-08 19:41:23 +00001214 // Check if the transfomation, or the original path itself
1215 // made us empty. Note this can also happen if we contained NaN
1216 // values. computing the bounds detects this, and will set our
1217 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1218 if (devPath.getBounds().isEmpty()) {
1219 // resetting the path will remove any NaN or other wanky values
1220 // that might upset our scan converter.
1221 devPath.reset();
1222 }
1223
reed@google.com5c3d1472011-02-22 19:12:23 +00001224 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001225 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001226
reed@google.com00177082011-10-12 14:34:30 +00001227 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228}
1229
1230bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001231 AutoValidateClip avc(this);
1232
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 fDeviceCMDirty = true;
1234 fLocalBoundsCompareTypeDirty = true;
1235
reed@google.com5c3d1472011-02-22 19:12:23 +00001236 // todo: signal fClipStack that we have a region, and therefore (I guess)
1237 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001238 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001239
reed@google.com00177082011-10-12 14:34:30 +00001240 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241}
1242
reed@google.com819c9212011-02-23 18:56:55 +00001243#ifdef SK_DEBUG
1244void SkCanvas::validateClip() const {
1245 // construct clipRgn from the clipstack
1246 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001247 if (!device) {
1248 SkASSERT(this->getTotalClip().isEmpty());
1249 return;
1250 }
1251
reed@google.com819c9212011-02-23 18:56:55 +00001252 SkIRect ir;
1253 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001254 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001255
robertphillips@google.com80214e22012-07-20 15:33:18 +00001256 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001257 const SkClipStack::Element* element;
1258 while ((element = iter.next()) != NULL) {
1259 switch (element->getType()) {
1260 case SkClipStack::Element::kPath_Type:
1261 clipPathHelper(this,
1262 &tmpClip,
1263 element->getPath(),
1264 element->getOp(),
1265 element->isAA());
1266 break;
1267 case SkClipStack::Element::kRect_Type:
1268 element->getRect().round(&ir);
1269 tmpClip.op(ir, element->getOp());
1270 break;
1271 case SkClipStack::Element::kEmpty_Type:
1272 tmpClip.setEmpty();
1273 break;
reed@google.com819c9212011-02-23 18:56:55 +00001274 }
1275 }
1276
reed@google.com6f8f2922011-03-04 22:27:10 +00001277#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001278 // now compare against the current rgn
1279 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001280 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001281#endif
reed@google.com819c9212011-02-23 18:56:55 +00001282}
1283#endif
1284
reed@google.com90c07ea2012-04-13 13:50:27 +00001285void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001286 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001287 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001288
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001289 static const SkRect kEmpty = { 0, 0, 0, 0 };
1290 while ((element = iter.next()) != NULL) {
1291 switch (element->getType()) {
1292 case SkClipStack::Element::kPath_Type:
1293 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1294 break;
1295 case SkClipStack::Element::kRect_Type:
1296 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1297 break;
1298 case SkClipStack::Element::kEmpty_Type:
1299 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1300 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001301 }
1302 }
1303}
1304
reed@google.com5c3d1472011-02-22 19:12:23 +00001305///////////////////////////////////////////////////////////////////////////////
1306
reed@google.com3b3e8952012-08-16 20:53:31 +00001307void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001309
reed@google.com3b3e8952012-08-16 20:53:31 +00001310 if (!this->getClipBounds(&r)) {
1311 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001313 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1314 SkScalarToCompareType(r.fTop),
1315 SkScalarToCompareType(r.fRight),
1316 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 }
1318}
1319
reed@google.com3b3e8952012-08-16 20:53:31 +00001320bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001321
reed@google.com16078632011-12-06 18:56:37 +00001322 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001323 return true;
1324
reed@google.com00177082011-10-12 14:34:30 +00001325 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 return true;
1327 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328
tomhudson@google.com8d430182011-06-06 19:11:19 +00001329 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001330 SkRect dst;
1331 fMCRec->fMatrix->mapRect(&dst, rect);
1332 SkIRect idst;
1333 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001334 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001335 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001336 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001337
reed@android.coma380ae42009-07-21 01:17:02 +00001338 // for speed, do the most likely reject compares first
1339 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1340 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1341 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1342 return true;
1343 }
1344 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1345 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1346 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1347 return true;
1348 }
1349 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351}
1352
reed@google.com3b3e8952012-08-16 20:53:31 +00001353bool SkCanvas::quickReject(const SkPath& path) const {
1354 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001357static inline int pinIntForScalar(int x) {
1358#ifdef SK_SCALAR_IS_FIXED
1359 if (x < SK_MinS16) {
1360 x = SK_MinS16;
1361 } else if (x > SK_MaxS16) {
1362 x = SK_MaxS16;
1363 }
1364#endif
1365 return x;
1366}
1367
reed@google.com3b3e8952012-08-16 20:53:31 +00001368bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001369 SkIRect ibounds;
1370 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 return false;
1372 }
1373
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001374 SkMatrix inverse;
1375 // if we can't invert the CTM, we can't return local clip bounds
1376 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001377 if (bounds) {
1378 bounds->setEmpty();
1379 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001380 return false;
1381 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001383 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001384 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001385 // adjust it outwards in case we are antialiasing
1386 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001387
1388 // SkRect::iset() will correctly assert if we pass a value out of range
1389 // (when SkScalar==fixed), so we pin to legal values. This does not
1390 // really returnt the correct answer, but its the best we can do given
1391 // that we've promised to return SkRect (even though we support devices
1392 // that can be larger than 32K in width or height).
1393 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1394 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001395 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001396 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 inverse.mapRect(bounds, r);
1398 }
1399 return true;
1400}
1401
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001402bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001403 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001404 if (clip.isEmpty()) {
1405 if (bounds) {
1406 bounds->setEmpty();
1407 }
1408 return false;
1409 }
1410
1411 if (NULL != bounds) {
1412 *bounds = clip.getBounds();
1413 }
1414 return true;
1415}
1416
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417const SkMatrix& SkCanvas::getTotalMatrix() const {
1418 return *fMCRec->fMatrix;
1419}
1420
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001421SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001422 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1423 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001424 return kComplex_ClipType;
1425}
1426
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001428 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429}
1430
bsalomon@google.come97f0852011-06-17 13:10:25 +00001431SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1432 int width, int height,
1433 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001434 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001435 if (device) {
1436 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1437 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001438 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001439 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001440 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441}
1442
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001443SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001444 int width, int height,
1445 bool isOpaque) {
1446 SkDevice* device = this->getDevice();
1447 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001448 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001449 } else {
1450 return NULL;
1451 }
1452}
1453
1454
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455//////////////////////////////////////////////////////////////////////////////
1456// These are the virtual drawing methods
1457//////////////////////////////////////////////////////////////////////////////
1458
reed@google.com2a981812011-04-14 18:59:28 +00001459void SkCanvas::clear(SkColor color) {
1460 SkDrawIter iter(this);
1461
1462 while (iter.next()) {
1463 iter.fDevice->clear(color);
1464 }
1465}
1466
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001468 this->internalDrawPaint(paint);
1469}
1470
1471void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001472 CHECK_SHADER_NOSETCONTEXT(paint);
1473
reed@google.com4e2b3d32011-04-07 14:18:59 +00001474 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475
1476 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001477 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 }
1479
reed@google.com4e2b3d32011-04-07 14:18:59 +00001480 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481}
1482
1483void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1484 const SkPaint& paint) {
1485 if ((long)count <= 0) {
1486 return;
1487 }
1488
reed@google.comea033602012-12-14 13:13:55 +00001489 CHECK_SHADER_NOSETCONTEXT(paint);
1490
reed@google.coma584aed2012-05-16 14:06:02 +00001491 if (paint.canComputeFastBounds()) {
1492 SkRect r;
1493 // special-case 2 points (common for drawing a single line)
1494 if (2 == count) {
1495 r.set(pts[0], pts[1]);
1496 } else {
1497 r.set(pts, count);
1498 }
1499 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001500 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001501 return;
1502 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001503 }
reed@google.coma584aed2012-05-16 14:06:02 +00001504
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 SkASSERT(pts != NULL);
1506
reed@google.com4e2b3d32011-04-07 14:18:59 +00001507 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001508
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001510 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 }
reed@google.com4b226022011-01-11 18:32:13 +00001512
reed@google.com4e2b3d32011-04-07 14:18:59 +00001513 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
1516void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001517 CHECK_SHADER_NOSETCONTEXT(paint);
1518
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519 if (paint.canComputeFastBounds()) {
1520 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001521 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 return;
1523 }
1524 }
reed@google.com4b226022011-01-11 18:32:13 +00001525
reed@google.com4e2b3d32011-04-07 14:18:59 +00001526 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527
1528 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001529 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530 }
1531
reed@google.com4e2b3d32011-04-07 14:18:59 +00001532 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533}
1534
reed@google.com4ed0fb72012-12-12 20:48:18 +00001535void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001536 CHECK_SHADER_NOSETCONTEXT(paint);
1537
reed@google.com4ed0fb72012-12-12 20:48:18 +00001538 if (paint.canComputeFastBounds()) {
1539 SkRect storage;
1540 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1541 return;
1542 }
1543 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001544
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001545 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1546
1547 while (iter.next()) {
1548 iter.fDevice->drawOval(iter, oval, looper.paint());
1549 }
1550
1551 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001552}
1553
1554void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001555 CHECK_SHADER_NOSETCONTEXT(paint);
1556
reed@google.com4ed0fb72012-12-12 20:48:18 +00001557 if (paint.canComputeFastBounds()) {
1558 SkRect storage;
1559 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1560 return;
1561 }
1562 }
1563
1564 if (rrect.isRect()) {
1565 // call the non-virtual version
1566 this->SkCanvas::drawRect(rrect.getBounds(), paint);
1567 } else {
1568 SkPath path;
1569 path.addRRect(rrect);
1570 // call the non-virtual version
1571 this->SkCanvas::drawPath(path, paint);
1572 }
1573}
1574
1575
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001577 CHECK_SHADER_NOSETCONTEXT(paint);
1578
reed@google.com93645112012-07-26 16:11:47 +00001579 if (!path.isFinite()) {
1580 return;
1581 }
1582
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001583 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001584 SkRect storage;
1585 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001586 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587 return;
1588 }
1589 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001590 if (path.isEmpty()) {
1591 if (path.isInverseFillType()) {
1592 this->internalDrawPaint(paint);
1593 }
1594 return;
1595 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596
reed@google.com4e2b3d32011-04-07 14:18:59 +00001597 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598
1599 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001600 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601 }
1602
reed@google.com4e2b3d32011-04-07 14:18:59 +00001603 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604}
1605
1606void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1607 const SkPaint* paint) {
1608 SkDEBUGCODE(bitmap.validate();)
1609
reed@google.com3d608122011-11-21 15:16:16 +00001610 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001611 SkRect bounds = {
1612 x, y,
1613 x + SkIntToScalar(bitmap.width()),
1614 y + SkIntToScalar(bitmap.height())
1615 };
1616 if (paint) {
1617 (void)paint->computeFastBounds(bounds, &bounds);
1618 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001619 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 return;
1621 }
1622 }
reed@google.com4b226022011-01-11 18:32:13 +00001623
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 SkMatrix matrix;
1625 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001626 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627}
1628
reed@google.com9987ec32011-09-07 11:57:52 +00001629// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001630void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001631 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1633 return;
1634 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001635
reed@google.comea033602012-12-14 13:13:55 +00001636 CHECK_LOCKCOUNT_BALANCE(bitmap);
1637
reed@google.com3d608122011-11-21 15:16:16 +00001638 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001639 SkRect storage;
1640 const SkRect* bounds = &dst;
1641 if (paint) {
1642 bounds = &paint->computeFastBounds(dst, &storage);
1643 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001644 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001645 return;
1646 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647 }
reed@google.com3d608122011-11-21 15:16:16 +00001648
reed@google.com33535f32012-09-25 15:37:50 +00001649 SkLazyPaint lazy;
1650 if (NULL == paint) {
1651 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001653
reed@google.com33535f32012-09-25 15:37:50 +00001654 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001655
reed@google.com33535f32012-09-25 15:37:50 +00001656 while (iter.next()) {
1657 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001658 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001659
reed@google.com33535f32012-09-25 15:37:50 +00001660 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661}
1662
reed@google.com71121732012-09-18 15:14:33 +00001663void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1664 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001665 SkDEBUGCODE(bitmap.validate();)
1666 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1667}
1668
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1670 const SkPaint* paint) {
1671 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001672 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673}
1674
reed@android.comf2b98d62010-12-20 18:26:13 +00001675void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1676 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001678 CHECK_LOCKCOUNT_BALANCE(bitmap);
reed@android.com9b039062009-02-11 15:09:58 +00001679
reed@google.com4e2b3d32011-04-07 14:18:59 +00001680 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001681
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001683 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684 }
reed@android.com9b039062009-02-11 15:09:58 +00001685
reed@google.com4e2b3d32011-04-07 14:18:59 +00001686 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687}
1688
reed@google.com9987ec32011-09-07 11:57:52 +00001689void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1690 const SkIRect& center, const SkRect& dst,
1691 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001692 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001693 SkRect storage;
1694 const SkRect* bounds = &dst;
1695 if (paint) {
1696 bounds = &paint->computeFastBounds(dst, &storage);
1697 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001698 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001699 return;
1700 }
1701 }
1702
reed@google.com9987ec32011-09-07 11:57:52 +00001703 const int32_t w = bitmap.width();
1704 const int32_t h = bitmap.height();
1705
1706 SkIRect c = center;
1707 // pin center to the bounds of the bitmap
1708 c.fLeft = SkMax32(0, center.fLeft);
1709 c.fTop = SkMax32(0, center.fTop);
1710 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1711 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1712
reed@google.com71121732012-09-18 15:14:33 +00001713 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001714 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001715 };
1716 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001717 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001718 };
reed@google.com9987ec32011-09-07 11:57:52 +00001719 SkScalar dstX[4] = {
1720 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1721 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1722 };
1723 SkScalar dstY[4] = {
1724 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1725 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1726 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001727
reed@google.com9987ec32011-09-07 11:57:52 +00001728 if (dstX[1] > dstX[2]) {
1729 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1730 dstX[2] = dstX[1];
1731 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001732
reed@google.com9987ec32011-09-07 11:57:52 +00001733 if (dstY[1] > dstY[2]) {
1734 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1735 dstY[2] = dstY[1];
1736 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001737
reed@google.com9987ec32011-09-07 11:57:52 +00001738 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001739 SkRect s, d;
1740
reed@google.com9987ec32011-09-07 11:57:52 +00001741 s.fTop = srcY[y];
1742 s.fBottom = srcY[y+1];
1743 d.fTop = dstY[y];
1744 d.fBottom = dstY[y+1];
1745 for (int x = 0; x < 3; x++) {
1746 s.fLeft = srcX[x];
1747 s.fRight = srcX[x+1];
1748 d.fLeft = dstX[x];
1749 d.fRight = dstX[x+1];
1750 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1751 }
1752 }
1753}
1754
1755void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1756 const SkRect& dst, const SkPaint* paint) {
1757 SkDEBUGCODE(bitmap.validate();)
1758
1759 // Need a device entry-point, so gpu can use a mesh
1760 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1761}
1762
reed@google.comf67e4cf2011-03-15 20:56:58 +00001763class SkDeviceFilteredPaint {
1764public:
1765 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1766 SkDevice::TextFlags flags;
1767 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001768 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001769 newPaint->setFlags(flags.fFlags);
1770 newPaint->setHinting(flags.fHinting);
1771 fPaint = newPaint;
1772 } else {
1773 fPaint = &paint;
1774 }
1775 }
1776
reed@google.comf67e4cf2011-03-15 20:56:58 +00001777 const SkPaint& paint() const { return *fPaint; }
1778
1779private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001780 const SkPaint* fPaint;
1781 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001782};
1783
bungeman@google.com52c748b2011-08-22 21:30:43 +00001784void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1785 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001786 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001787 draw.fDevice->drawRect(draw, r, paint);
1788 } else {
1789 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001790 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001791 draw.fDevice->drawRect(draw, r, p);
1792 }
1793}
1794
1795void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1796 const char text[], size_t byteLength,
1797 SkScalar x, SkScalar y) {
1798 SkASSERT(byteLength == 0 || text != NULL);
1799
1800 // nothing to draw
1801 if (text == NULL || byteLength == 0 ||
1802 draw.fClip->isEmpty() ||
1803 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1804 return;
1805 }
1806
1807 SkScalar width = 0;
1808 SkPoint start;
1809
1810 start.set(0, 0); // to avoid warning
1811 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1812 SkPaint::kStrikeThruText_Flag)) {
1813 width = paint.measureText(text, byteLength);
1814
1815 SkScalar offsetX = 0;
1816 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1817 offsetX = SkScalarHalf(width);
1818 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1819 offsetX = width;
1820 }
1821 start.set(x - offsetX, y);
1822 }
1823
1824 if (0 == width) {
1825 return;
1826 }
1827
1828 uint32_t flags = paint.getFlags();
1829
1830 if (flags & (SkPaint::kUnderlineText_Flag |
1831 SkPaint::kStrikeThruText_Flag)) {
1832 SkScalar textSize = paint.getTextSize();
1833 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1834 SkRect r;
1835
1836 r.fLeft = start.fX;
1837 r.fRight = start.fX + width;
1838
1839 if (flags & SkPaint::kUnderlineText_Flag) {
1840 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1841 start.fY);
1842 r.fTop = offset;
1843 r.fBottom = offset + height;
1844 DrawRect(draw, paint, r, textSize);
1845 }
1846 if (flags & SkPaint::kStrikeThruText_Flag) {
1847 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1848 start.fY);
1849 r.fTop = offset;
1850 r.fBottom = offset + height;
1851 DrawRect(draw, paint, r, textSize);
1852 }
1853 }
1854}
1855
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856void SkCanvas::drawText(const void* text, size_t byteLength,
1857 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001858 CHECK_SHADER_NOSETCONTEXT(paint);
1859
reed@google.com4e2b3d32011-04-07 14:18:59 +00001860 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861
1862 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001863 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001864 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001865 DrawTextDecorations(iter, dfp.paint(),
1866 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 }
1868
reed@google.com4e2b3d32011-04-07 14:18:59 +00001869 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870}
1871
1872void SkCanvas::drawPosText(const void* text, size_t byteLength,
1873 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001874 CHECK_SHADER_NOSETCONTEXT(paint);
1875
reed@google.com4e2b3d32011-04-07 14:18:59 +00001876 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001877
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001879 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001881 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 }
reed@google.com4b226022011-01-11 18:32:13 +00001883
reed@google.com4e2b3d32011-04-07 14:18:59 +00001884 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885}
1886
1887void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1888 const SkScalar xpos[], SkScalar constY,
1889 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001890 CHECK_SHADER_NOSETCONTEXT(paint);
1891
reed@google.com4e2b3d32011-04-07 14:18:59 +00001892 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001893
reed@android.com8a1c16f2008-12-17 15:59:43 +00001894 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001895 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001897 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898 }
reed@google.com4b226022011-01-11 18:32:13 +00001899
reed@google.com4e2b3d32011-04-07 14:18:59 +00001900 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901}
1902
1903void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1904 const SkPath& path, const SkMatrix* matrix,
1905 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001906 CHECK_SHADER_NOSETCONTEXT(paint);
1907
reed@google.com4e2b3d32011-04-07 14:18:59 +00001908 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909
1910 while (iter.next()) {
1911 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001912 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 }
1914
reed@google.com4e2b3d32011-04-07 14:18:59 +00001915 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916}
1917
djsollen@google.com56c69772011-11-08 19:00:26 +00001918#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001919void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1920 const SkPoint pos[], const SkPaint& paint,
1921 const SkPath& path, const SkMatrix* matrix) {
reed@google.comea033602012-12-14 13:13:55 +00001922 CHECK_SHADER_NOSETCONTEXT(paint);
1923
reed@google.com4e2b3d32011-04-07 14:18:59 +00001924 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001925
1926 while (iter.next()) {
1927 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001928 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001929 }
1930
reed@google.com4e2b3d32011-04-07 14:18:59 +00001931 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001932}
1933#endif
1934
reed@android.com8a1c16f2008-12-17 15:59:43 +00001935void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1936 const SkPoint verts[], const SkPoint texs[],
1937 const SkColor colors[], SkXfermode* xmode,
1938 const uint16_t indices[], int indexCount,
1939 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001940 CHECK_SHADER_NOSETCONTEXT(paint);
1941
reed@google.com4e2b3d32011-04-07 14:18:59 +00001942 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001943
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 while (iter.next()) {
1945 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001946 colors, xmode, indices, indexCount,
1947 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948 }
reed@google.com4b226022011-01-11 18:32:13 +00001949
reed@google.com4e2b3d32011-04-07 14:18:59 +00001950 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951}
1952
reed@android.comcb608442009-12-04 21:32:27 +00001953void SkCanvas::drawData(const void* data, size_t length) {
1954 // do nothing. Subclasses may do something with the data
1955}
1956
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957//////////////////////////////////////////////////////////////////////////////
1958// These methods are NOT virtual, and therefore must call back into virtual
1959// methods, rather than actually drawing themselves.
1960//////////////////////////////////////////////////////////////////////////////
1961
1962void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001963 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964 SkPaint paint;
1965
1966 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001967 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001968 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969 }
1970 this->drawPaint(paint);
1971}
1972
reed@android.com845fdac2009-06-23 03:01:32 +00001973void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974 SkPaint paint;
1975
1976 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001977 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001978 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 }
1980 this->drawPaint(paint);
1981}
1982
1983void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1984 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001985
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986 pt.set(x, y);
1987 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1988}
1989
1990void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1991 SkPoint pt;
1992 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001993
reed@android.com8a1c16f2008-12-17 15:59:43 +00001994 pt.set(x, y);
1995 paint.setColor(color);
1996 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1997}
1998
1999void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2000 const SkPaint& paint) {
2001 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002002
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003 pts[0].set(x0, y0);
2004 pts[1].set(x1, y1);
2005 this->drawPoints(kLines_PointMode, 2, pts, paint);
2006}
2007
2008void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2009 SkScalar right, SkScalar bottom,
2010 const SkPaint& paint) {
2011 SkRect r;
2012
2013 r.set(left, top, right, bottom);
2014 this->drawRect(r, paint);
2015}
2016
2017void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2018 const SkPaint& paint) {
2019 if (radius < 0) {
2020 radius = 0;
2021 }
2022
2023 SkRect r;
2024 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002025 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026}
2027
2028void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2029 const SkPaint& paint) {
2030 if (rx > 0 && ry > 0) {
2031 if (paint.canComputeFastBounds()) {
2032 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002033 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034 return;
2035 }
2036 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002037 SkRRect rrect;
2038 rrect.setRectXY(r, rx, ry);
2039 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 } else {
2041 this->drawRect(r, paint);
2042 }
2043}
2044
reed@android.com8a1c16f2008-12-17 15:59:43 +00002045void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2046 SkScalar sweepAngle, bool useCenter,
2047 const SkPaint& paint) {
2048 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2049 this->drawOval(oval, paint);
2050 } else {
2051 SkPath path;
2052 if (useCenter) {
2053 path.moveTo(oval.centerX(), oval.centerY());
2054 }
2055 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2056 if (useCenter) {
2057 path.close();
2058 }
2059 this->drawPath(path, paint);
2060 }
2061}
2062
2063void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2064 const SkPath& path, SkScalar hOffset,
2065 SkScalar vOffset, const SkPaint& paint) {
2066 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002067
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068 matrix.setTranslate(hOffset, vOffset);
2069 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2070}
2071
reed@android.comf76bacf2009-05-13 14:00:33 +00002072///////////////////////////////////////////////////////////////////////////////
2073
reed@android.com8a1c16f2008-12-17 15:59:43 +00002074void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076}
2077
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078///////////////////////////////////////////////////////////////////////////////
2079///////////////////////////////////////////////////////////////////////////////
2080
2081SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002082 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083
2084 SkASSERT(canvas);
2085
2086 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2087 fDone = !fImpl->next();
2088}
2089
2090SkCanvas::LayerIter::~LayerIter() {
2091 fImpl->~SkDrawIter();
2092}
2093
2094void SkCanvas::LayerIter::next() {
2095 fDone = !fImpl->next();
2096}
2097
2098SkDevice* SkCanvas::LayerIter::device() const {
2099 return fImpl->getDevice();
2100}
2101
2102const SkMatrix& SkCanvas::LayerIter::matrix() const {
2103 return fImpl->getMatrix();
2104}
2105
2106const SkPaint& SkCanvas::LayerIter::paint() const {
2107 const SkPaint* paint = fImpl->getPaint();
2108 if (NULL == paint) {
2109 paint = &fDefaultPaint;
2110 }
2111 return *paint;
2112}
2113
2114const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2115int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2116int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002117
2118///////////////////////////////////////////////////////////////////////////////
2119
2120SkCanvas::ClipVisitor::~ClipVisitor() { }
reed@google.com97af1a62012-08-28 12:19:02 +00002121
2122