blob: f563cd1211f488d44a0b78e3ee2b8f21d3fd6711 [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"
13#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
16#include "SkPicture.h"
17#include "SkScalarCompare.h"
18#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000019#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000020#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022
23//#define SK_TRACE_SAVERESTORE
24
25#ifdef SK_TRACE_SAVERESTORE
26 static int gLayerCounter;
27 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
28 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
29
30 static int gRecCounter;
31 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
32 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
33
34 static int gCanvasCounter;
35 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
36 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
37#else
38 #define inc_layer()
39 #define dec_layer()
40 #define inc_rec()
41 #define dec_rec()
42 #define inc_canvas()
43 #define dec_canvas()
44#endif
45
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000046typedef SkTLazy<SkPaint> SkLazyPaint;
47
reed@android.com8a1c16f2008-12-17 15:59:43 +000048///////////////////////////////////////////////////////////////////////////////
49// Helpers for computing fast bounds for quickReject tests
50
51static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
52 return paint != NULL && paint->isAntiAlias() ?
53 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
54}
55
56///////////////////////////////////////////////////////////////////////////////
57
58/* This is the record we keep for each SkDevice that the user installs.
59 The clip/matrix/proc are fields that reflect the top of the save/restore
60 stack. Whenever the canvas changes, it marks a dirty flag, and then before
61 these are used (assuming we're not on a layer) we rebuild these cache
62 values: they reflect the top of the save stack, but translated and clipped
63 by the device's XY offset and bitmap-bounds.
64*/
65struct DeviceCM {
66 DeviceCM* fNext;
67 SkDevice* fDevice;
68 SkRegion fClip;
69 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000070 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000071 // optional, related to canvas' external matrix
72 const SkMatrix* fMVMatrix;
73 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000074
bungeman@google.com88edf1e2011-08-08 19:41:56 +000075 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 : fNext(NULL) {
77 if (NULL != device) {
78 device->ref();
79 device->lockPixels();
80 }
reed@google.com4b226022011-01-11 18:32:13 +000081 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000084
bungeman@google.com88edf1e2011-08-08 19:41:56 +000085 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 if (NULL != fDevice) {
87 fDevice->unlockPixels();
88 fDevice->unref();
89 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 SkDELETE(fPaint);
91 }
reed@google.com4b226022011-01-11 18:32:13 +000092
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +000094 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000095 int x = fDevice->getOrigin().x();
96 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 int width = fDevice->width();
98 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 if ((x | y) == 0) {
101 fMatrix = &totalMatrix;
102 fClip = totalClip;
103 } else {
104 fMatrixStorage = totalMatrix;
105 fMatrixStorage.postTranslate(SkIntToScalar(-x),
106 SkIntToScalar(-y));
107 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 totalClip.translate(-x, -y, &fClip);
110 }
111
112 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
113
114 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 if (updateClip) {
117 updateClip->op(x, y, x + width, y + height,
118 SkRegion::kDifference_Op);
119 }
reed@google.com4b226022011-01-11 18:32:13 +0000120
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000121 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122
123#ifdef SK_DEBUG
124 if (!fClip.isEmpty()) {
125 SkIRect deviceR;
126 deviceR.set(0, 0, width, height);
127 SkASSERT(deviceR.contains(fClip.getBounds()));
128 }
129#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000130 // default is to assume no external matrix
131 fMVMatrix = NULL;
132 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000134
135 // can only be called after calling updateMC()
136 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
137 fMVMatrixStorage.setConcat(extI, *fMatrix);
138 fMVMatrix = &fMVMatrixStorage;
139 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
140 }
141
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000143 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144};
145
146/* This is the record we keep for each save/restore level in the stack.
147 Since a level optionally copies the matrix and/or stack, we have pointers
148 for these fields. If the value is copied for this level, the copy is
149 stored in the ...Storage field, and the pointer points to that. If the
150 value is not copied for this level, we ignore ...Storage, and just point
151 at the corresponding value in the previous level in the stack.
152*/
153class SkCanvas::MCRec {
154public:
155 MCRec* fNext;
156 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
157 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
158 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 DeviceCM* fLayer;
161 /* If there are any layers in the stack, this points to the top-most
162 one that is at or below this level in the stack (so we know what
163 bitmap/device to draw into from this level. This value is NOT
164 reference counted, since the real owner is either our fLayer field,
165 or a previous one in a lower level.)
166 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000167 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
169 MCRec(const MCRec* prev, int flags) {
170 if (NULL != prev) {
171 if (flags & SkCanvas::kMatrix_SaveFlag) {
172 fMatrixStorage = *prev->fMatrix;
173 fMatrix = &fMatrixStorage;
174 } else {
175 fMatrix = prev->fMatrix;
176 }
reed@google.com4b226022011-01-11 18:32:13 +0000177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 if (flags & SkCanvas::kClip_SaveFlag) {
179 fRegionStorage = *prev->fRegion;
180 fRegion = &fRegionStorage;
181 } else {
182 fRegion = prev->fRegion;
183 }
184
185 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000186 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187
188 fTopLayer = prev->fTopLayer;
189 } else { // no prev
190 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 fMatrix = &fMatrixStorage;
193 fRegion = &fRegionStorage;
194 fFilter = NULL;
195 fTopLayer = NULL;
196 }
197 fLayer = NULL;
198
199 // don't bother initializing fNext
200 inc_rec();
201 }
202 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000203 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 SkDELETE(fLayer);
205 dec_rec();
206 }
reed@google.com4b226022011-01-11 18:32:13 +0000207
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208private:
209 SkMatrix fMatrixStorage;
210 SkRegion fRegionStorage;
211};
212
213class SkDrawIter : public SkDraw {
214public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000215 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
216 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 canvas->updateDeviceCMCache();
218
reed@google.com7d7ca792011-02-23 22:39:18 +0000219 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fBounder = canvas->getBounder();
221 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000222 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 }
reed@google.com4b226022011-01-11 18:32:13 +0000224
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 bool next() {
226 // skip over recs with empty clips
227 if (fSkipEmptyClips) {
228 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
229 fCurrLayer = fCurrLayer->fNext;
230 }
231 }
232
233 if (NULL != fCurrLayer) {
234 const DeviceCM* rec = fCurrLayer;
235
236 fMatrix = rec->fMatrix;
237 fClip = &rec->fClip;
238 fDevice = rec->fDevice;
239 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000241 fMVMatrix = rec->fMVMatrix;
242 fExtMatrix = rec->fExtMatrix;
243 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 fCurrLayer = rec->fNext;
246 if (fBounder) {
247 fBounder->setClip(fClip);
248 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000250
bsalomon@google.comd302f142011-03-03 13:54:13 +0000251 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 return true;
253 }
254 return false;
255 }
reed@google.com4b226022011-01-11 18:32:13 +0000256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000258 int getX() const { return fDevice->getOrigin().x(); }
259 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 const SkMatrix& getMatrix() const { return *fMatrix; }
261 const SkRegion& getClip() const { return *fClip; }
262 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264private:
265 SkCanvas* fCanvas;
266 const DeviceCM* fCurrLayer;
267 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 SkBool8 fSkipEmptyClips;
269
270 typedef SkDraw INHERITED;
271};
272
273/////////////////////////////////////////////////////////////////////////////
274
275class AutoDrawLooper {
276public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000277 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
278 fCanvas = canvas;
279 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000281 fPaint = NULL;
282 fSaveCount = canvas->getSaveCount();
283 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 if (fLooper) {
286 fLooper->init(canvas);
287 }
288 }
289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000291 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293
294 const SkPaint& paint() const {
295 SkASSERT(fPaint);
296 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000298
299 bool next(SkDrawFilter::Type drawType);
300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000302 SkLazyPaint fLazyPaint;
303 SkCanvas* fCanvas;
304 const SkPaint& fOrigPaint;
305 SkDrawLooper* fLooper;
306 SkDrawFilter* fFilter;
307 const SkPaint* fPaint;
308 int fSaveCount;
309 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310};
311
reed@google.com4e2b3d32011-04-07 14:18:59 +0000312bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000313 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000314 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000315 return false;
316 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000317
reed@google.com632e1a22011-10-06 12:37:00 +0000318 if (fLooper || fFilter) {
319 SkPaint* paint = fLazyPaint.set(fOrigPaint);
320 if (fLooper && !fLooper->next(fCanvas, paint)) {
321 fDone = true;
322 return false;
323 }
324 if (fFilter) {
325 fFilter->filter(paint, drawType);
326 if (NULL == fLooper) {
327 // no looper means we only draw once
328 fDone = true;
329 }
330 }
331 fPaint = paint;
332 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000333 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000334 fPaint = &fOrigPaint;
335 }
336
337 // call this after any possible paint modifiers
338 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000339 fPaint = NULL;
340 return false;
341 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 return true;
343}
344
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345/* Stack helper for managing a SkBounder. In the destructor, if we were
346 given a bounder, we call its commit() method, signifying that we are
347 done accumulating bounds for that draw.
348*/
349class SkAutoBounderCommit {
350public:
351 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
352 ~SkAutoBounderCommit() {
353 if (NULL != fBounder) {
354 fBounder->commit();
355 }
356 }
357private:
358 SkBounder* fBounder;
359};
360
361#include "SkColorPriv.h"
362
363class AutoValidator {
364public:
365 AutoValidator(SkDevice* device) : fDevice(device) {}
366 ~AutoValidator() {
367#ifdef SK_DEBUG
368 const SkBitmap& bm = fDevice->accessBitmap(false);
369 if (bm.config() == SkBitmap::kARGB_4444_Config) {
370 for (int y = 0; y < bm.height(); y++) {
371 const SkPMColor16* p = bm.getAddr16(0, y);
372 for (int x = 0; x < bm.width(); x++) {
373 SkPMColor16 c = p[x];
374 SkPMColor16Assert(c);
375 }
376 }
377 }
378#endif
379 }
380private:
381 SkDevice* fDevice;
382};
383
384////////// macros to place around the internal draw calls //////////////////
385
reed@google.com4e2b3d32011-04-07 14:18:59 +0000386#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000388 AutoDrawLooper looper(this, paint); \
389 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 SkAutoBounderCommit ac(fBounder); \
391 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000392
reed@google.com4e2b3d32011-04-07 14:18:59 +0000393#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394
395////////////////////////////////////////////////////////////////////////////
396
397SkDevice* SkCanvas::init(SkDevice* device) {
398 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000399 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000401 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000402 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000403 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000404 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405
406 fMCRec = (MCRec*)fMCStack.push_back();
407 new (fMCRec) MCRec(NULL, 0);
408
409 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
410 fMCRec->fTopLayer = fMCRec->fLayer;
411 fMCRec->fNext = NULL;
412
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000413 fExternalMatrix.reset();
414 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000415 fUseExternalMatrix = false;
416
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 return this->setDevice(device);
418}
419
reed@google.comcde92112011-07-06 20:00:52 +0000420SkCanvas::SkCanvas()
421: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000422 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000423
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000424 this->init(NULL);
425}
426
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000428 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429 inc_canvas();
430
431 this->init(device);
432}
433
434SkCanvas::SkCanvas(const SkBitmap& bitmap)
435 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
436 inc_canvas();
437
reed@google.comcde92112011-07-06 20:00:52 +0000438 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439}
440
441SkCanvas::~SkCanvas() {
442 // free up the contents of our deque
443 this->restoreToCount(1); // restore everything but the last
444 this->internalRestore(); // restore the last, since we're going away
445
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000446 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 dec_canvas();
449}
450
451SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
452 SkRefCnt_SafeAssign(fBounder, bounder);
453 return bounder;
454}
455
456SkDrawFilter* SkCanvas::getDrawFilter() const {
457 return fMCRec->fFilter;
458}
459
460SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
461 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
462 return filter;
463}
464
465///////////////////////////////////////////////////////////////////////////////
466
467SkDevice* SkCanvas::getDevice() const {
468 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000469 SkDeque::F2BIter iter(fMCStack);
470 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 SkASSERT(rec && rec->fLayer);
472 return rec->fLayer->fDevice;
473}
474
reed@google.com9266fed2011-03-30 00:18:03 +0000475SkDevice* SkCanvas::getTopDevice() const {
476 return fMCRec->fTopLayer->fDevice;
477}
478
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479SkDevice* SkCanvas::setDevice(SkDevice* device) {
480 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000481 SkDeque::F2BIter iter(fMCStack);
482 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 SkASSERT(rec && rec->fLayer);
484 SkDevice* rootDevice = rec->fLayer->fDevice;
485
486 if (rootDevice == device) {
487 return device;
488 }
reed@google.com4b226022011-01-11 18:32:13 +0000489
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 /* Notify the devices that they are going in/out of scope, so they can do
491 things like lock/unlock their pixels, etc.
492 */
493 if (device) {
494 device->lockPixels();
495 }
496 if (rootDevice) {
497 rootDevice->unlockPixels();
498 }
499
500 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
501 rootDevice = device;
502
503 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 /* Now we update our initial region to have the bounds of the new device,
506 and then intersect all of the clips in our stack with these bounds,
507 to ensure that we can't draw outside of the device's bounds (and trash
508 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 NOTE: this is only a partial-fix, since if the new device is larger than
511 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000512 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
514 reconstruct the correct clips, so this approximation will have to do.
515 The caller really needs to restore() back to the base if they want to
516 accurately take advantage of the new device bounds.
517 */
518
519 if (NULL == device) {
520 rec->fRegion->setEmpty();
521 while ((rec = (MCRec*)iter.next()) != NULL) {
522 (void)rec->fRegion->setEmpty();
523 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000524 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 } else {
526 // compute our total bounds for all devices
527 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 bounds.set(0, 0, device->width(), device->height());
530
531 // now jam our 1st clip to be bounds, and intersect the rest with that
532 rec->fRegion->setRect(bounds);
533 while ((rec = (MCRec*)iter.next()) != NULL) {
534 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
535 }
536 }
537 return device;
538}
539
reed@google.comaf951c92011-06-16 19:10:39 +0000540SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
541 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 device->unref();
543 return device;
544}
545
reed@google.com51df9e32010-12-23 19:29:18 +0000546bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
547 SkDevice* device = this->getDevice();
548 if (!device) {
549 return false;
550 }
551 return device->readPixels(srcRect, bitmap);
552}
553
reed@google.com4b226022011-01-11 18:32:13 +0000554//////////////////////////////////////////////////////////////////////////////
555
reed@google.com51df9e32010-12-23 19:29:18 +0000556bool SkCanvas::readPixels(SkBitmap* bitmap) {
557 SkDevice* device = this->getDevice();
558 if (!device) {
559 return false;
560 }
561 SkIRect bounds;
562 bounds.set(0, 0, device->width(), device->height());
563 return this->readPixels(bounds, bitmap);
564}
565
566void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
567 SkDevice* device = this->getDevice();
568 if (device) {
569 device->writePixels(bitmap, x, y);
570 }
571}
572
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573//////////////////////////////////////////////////////////////////////////////
574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575void SkCanvas::updateDeviceCMCache() {
576 if (fDeviceCMDirty) {
577 const SkMatrix& totalMatrix = this->getTotalMatrix();
578 const SkRegion& totalClip = this->getTotalClip();
579 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000580
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000582 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000583 if (fUseExternalMatrix) {
584 layer->updateExternalMatrix(fExternalMatrix,
585 fExternalInverse);
586 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 } else {
588 SkRegion clip;
589 clip = totalClip; // make a copy
590 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000591 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000592 if (fUseExternalMatrix) {
593 layer->updateExternalMatrix(fExternalMatrix,
594 fExternalInverse);
595 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 } while ((layer = layer->fNext) != NULL);
597 }
598 fDeviceCMDirty = false;
599 }
600}
601
reed@android.comf2b98d62010-12-20 18:26:13 +0000602void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000603 const SkRegion& clip,
604 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000606 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000607 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000608 fLastDeviceToGainFocus = device;
609 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610}
611
612///////////////////////////////////////////////////////////////////////////////
613
614int SkCanvas::internalSave(SaveFlags flags) {
615 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000616
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 MCRec* newTop = (MCRec*)fMCStack.push_back();
618 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 newTop->fNext = fMCRec;
621 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000622
reed@google.com5c3d1472011-02-22 19:12:23 +0000623 fClipStack.save();
624 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
625
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 return saveCount;
627}
628
629int SkCanvas::save(SaveFlags flags) {
630 // call shared impl
631 return this->internalSave(flags);
632}
633
634#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
635#define C16MASK (1 << SkBitmap::kRGB_565_Config)
636#define C8MASK (1 << SkBitmap::kA8_Config)
637
638static SkBitmap::Config resolve_config(SkCanvas* canvas,
639 const SkIRect& bounds,
640 SkCanvas::SaveFlags flags,
641 bool* isOpaque) {
642 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
643
644#if 0
645 // loop through and union all the configs we may draw into
646 uint32_t configMask = 0;
647 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
648 {
649 SkDevice* device = canvas->getLayerDevice(i);
650 if (device->intersects(bounds))
651 configMask |= 1 << device->config();
652 }
653
654 // if the caller wants alpha or fullcolor, we can't return 565
655 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
656 SkCanvas::kHasAlphaLayer_SaveFlag))
657 configMask &= ~C16MASK;
658
659 switch (configMask) {
660 case C8MASK: // if we only have A8, return that
661 return SkBitmap::kA8_Config;
662
663 case C16MASK: // if we only have 565, return that
664 return SkBitmap::kRGB_565_Config;
665
666 default:
667 return SkBitmap::kARGB_8888_Config; // default answer
668 }
669#else
670 return SkBitmap::kARGB_8888_Config; // default answer
671#endif
672}
673
674static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
675 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
676}
677
678int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
679 SaveFlags flags) {
680 // do this before we create the layer. We don't call the public save() since
681 // that would invoke a possibly overridden virtual
682 int count = this->internalSave(flags);
683
684 fDeviceCMDirty = true;
685
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000686 SkIRect clipBounds;
687 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000688 return count;
689 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000691 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 if (NULL != bounds) {
693 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 this->getTotalMatrix().mapRect(&r, *bounds);
696 r.roundOut(&ir);
697 // early exit if the layer's bounds are clipped out
698 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000699 if (bounds_affects_clip(flags)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 fMCRec->fRegion->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000701 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 return count;
703 }
704 } else { // no user bounds, so just use the clip
705 ir = clipBounds;
706 }
707
reed@google.com5c3d1472011-02-22 19:12:23 +0000708 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709 // early exit if the clip is now empty
710 if (bounds_affects_clip(flags) &&
711 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
712 return count;
713 }
714
715 bool isOpaque;
716 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
717
bsalomon@google.come97f0852011-06-17 13:10:25 +0000718 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
719 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000720 if (NULL == device) {
721 SkDebugf("Unable to create device for layer.");
722 return count;
723 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000724
reed@google.com6f8f2922011-03-04 22:27:10 +0000725 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
727 device->unref();
728
729 layer->fNext = fMCRec->fTopLayer;
730 fMCRec->fLayer = layer;
731 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
732
733 return count;
734}
735
736int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
737 SaveFlags flags) {
738 if (0xFF == alpha) {
739 return this->saveLayer(bounds, NULL, flags);
740 } else {
741 SkPaint tmpPaint;
742 tmpPaint.setAlpha(alpha);
743 return this->saveLayer(bounds, &tmpPaint, flags);
744 }
745}
746
747void SkCanvas::restore() {
748 // check for underflow
749 if (fMCStack.count() > 1) {
750 this->internalRestore();
751 }
752}
753
754void SkCanvas::internalRestore() {
755 SkASSERT(fMCStack.count() != 0);
756
757 fDeviceCMDirty = true;
758 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000759 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760
reed@google.com5c3d1472011-02-22 19:12:23 +0000761 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000762 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763 DeviceCM* layer = fMCRec->fLayer; // may be null
764 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
765 fMCRec->fLayer = NULL;
766
767 // now do the normal restore()
768 fMCRec->~MCRec(); // balanced in save()
769 fMCStack.pop_back();
770 fMCRec = (MCRec*)fMCStack.back();
771
772 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
773 since if we're being recorded, we don't want to record this (the
774 recorder will have already recorded the restore).
775 */
776 if (NULL != layer) {
777 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000778 const SkIPoint& origin = layer->fDevice->getOrigin();
779 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 layer->fPaint);
781 // reset this, since drawDevice will have set it to true
782 fDeviceCMDirty = true;
783 }
784 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000785 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000786
787 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788}
789
790int SkCanvas::getSaveCount() const {
791 return fMCStack.count();
792}
793
794void SkCanvas::restoreToCount(int count) {
795 // sanity check
796 if (count < 1) {
797 count = 1;
798 }
799 while (fMCStack.count() > count) {
800 this->restore();
801 }
802}
803
804/////////////////////////////////////////////////////////////////////////////
805
806// can't draw it if its empty, or its too big for a fixed-point width or height
807static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000808 return bitmap.width() <= 0 || bitmap.height() <= 0
809#ifndef SK_ALLOW_OVER_32K_BITMAPS
810 || bitmap.width() > 32767 || bitmap.height() > 32767
811#endif
812 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813}
814
reed@android.comf2b98d62010-12-20 18:26:13 +0000815void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 const SkMatrix& matrix, const SkPaint* paint) {
817 if (reject_bitmap(bitmap)) {
818 return;
819 }
820
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000821 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000823 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000825 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826}
827
828void SkCanvas::drawDevice(SkDevice* device, int x, int y,
829 const SkPaint* paint) {
830 SkPaint tmp;
831 if (NULL == paint) {
832 tmp.setDither(true);
833 paint = &tmp;
834 }
reed@google.com4b226022011-01-11 18:32:13 +0000835
reed@google.com4e2b3d32011-04-07 14:18:59 +0000836 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 while (iter.next()) {
838 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000839 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000841 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842}
843
844/////////////////////////////////////////////////////////////////////////////
845
846bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
847 fDeviceCMDirty = true;
848 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000849 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 return fMCRec->fMatrix->preTranslate(dx, dy);
851}
852
853bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
854 fDeviceCMDirty = true;
855 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000856 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 return fMCRec->fMatrix->preScale(sx, sy);
858}
859
860bool SkCanvas::rotate(SkScalar degrees) {
861 fDeviceCMDirty = true;
862 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000863 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 return fMCRec->fMatrix->preRotate(degrees);
865}
866
867bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
868 fDeviceCMDirty = true;
869 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000870 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 return fMCRec->fMatrix->preSkew(sx, sy);
872}
873
874bool SkCanvas::concat(const SkMatrix& matrix) {
875 fDeviceCMDirty = true;
876 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000877 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 return fMCRec->fMatrix->preConcat(matrix);
879}
880
881void SkCanvas::setMatrix(const SkMatrix& matrix) {
882 fDeviceCMDirty = true;
883 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000884 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 *fMCRec->fMatrix = matrix;
886}
887
888// this is not virtual, so it must call a virtual method so that subclasses
889// will see its action
890void SkCanvas::resetMatrix() {
891 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000892
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 matrix.reset();
894 this->setMatrix(matrix);
895}
896
897//////////////////////////////////////////////////////////////////////////////
898
899bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000900 AutoValidateClip avc(this);
901
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 fDeviceCMDirty = true;
903 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000904 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905
906 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000907 // for these simpler matrices, we can stay a rect ever after applying
908 // the matrix. This means we don't have to a) make a path, and b) tell
909 // the region code to scan-convert the path, only to discover that it
910 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 SkRect r;
912 SkIRect ir;
913
914 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000915 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 r.round(&ir);
917 return fMCRec->fRegion->op(ir, op);
918 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000919 // since we're rotate or some such thing, we convert the rect to a path
920 // and clip against that, since it can handle any matrix. However, to
921 // avoid recursion in the case where we are subclassed (e.g. Pictures)
922 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 SkPath path;
924
925 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000926 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 }
928}
929
reed@google.com819c9212011-02-23 18:56:55 +0000930static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
931 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000932 // base is used to limit the size (and therefore memory allocation) of the
933 // region that results from scan converting devPath.
934 SkRegion base;
935
reed@google.com819c9212011-02-23 18:56:55 +0000936 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000937 // since we are intersect, we can do better (tighter) with currRgn's
938 // bounds, than just using the device. However, if currRgn is complex,
939 // our region blitter may hork, so we do that case in two steps.
940 if (currRgn->isRect()) {
941 return currRgn->setPath(devPath, *currRgn);
942 } else {
943 base.setRect(currRgn->getBounds());
944 SkRegion rgn;
945 rgn.setPath(devPath, base);
946 return currRgn->op(rgn, op);
947 }
reed@google.com819c9212011-02-23 18:56:55 +0000948 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000949 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
950 base.setRect(0, 0, bm.width(), bm.height());
951
952 if (SkRegion::kReplace_Op == op) {
953 return currRgn->setPath(devPath, base);
954 } else {
955 SkRegion rgn;
956 rgn.setPath(devPath, base);
957 return currRgn->op(rgn, op);
958 }
959 }
960}
961
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000963 AutoValidateClip avc(this);
964
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 fDeviceCMDirty = true;
966 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000967 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968
969 SkPath devPath;
970 path.transform(*fMCRec->fMatrix, &devPath);
971
reed@google.com5c3d1472011-02-22 19:12:23 +0000972 // if we called path.swap() we could avoid a deep copy of this path
973 fClipStack.clipDevPath(devPath, op);
974
reed@google.com819c9212011-02-23 18:56:55 +0000975 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976}
977
978bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000979 AutoValidateClip avc(this);
980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 fDeviceCMDirty = true;
982 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000983 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984
reed@google.com5c3d1472011-02-22 19:12:23 +0000985 // todo: signal fClipStack that we have a region, and therefore (I guess)
986 // we have to ignore it, and use the region directly?
987 fClipStack.clipDevRect(rgn.getBounds());
988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 return fMCRec->fRegion->op(rgn, op);
990}
991
reed@google.com819c9212011-02-23 18:56:55 +0000992#ifdef SK_DEBUG
993void SkCanvas::validateClip() const {
994 // construct clipRgn from the clipstack
995 const SkDevice* device = this->getDevice();
996 SkIRect ir;
997 ir.set(0, 0, device->width(), device->height());
998 SkRegion clipRgn(ir);
999
1000 SkClipStack::B2FIter iter(fClipStack);
1001 const SkClipStack::B2FIter::Clip* clip;
1002 while ((clip = iter.next()) != NULL) {
1003 if (clip->fPath) {
1004 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1005 } else if (clip->fRect) {
1006 clip->fRect->round(&ir);
1007 clipRgn.op(ir, clip->fOp);
1008 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001009 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001010 }
1011 }
1012
reed@google.com6f8f2922011-03-04 22:27:10 +00001013#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001014 // now compare against the current rgn
1015 const SkRegion& rgn = this->getTotalClip();
1016 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001017#endif
reed@google.com819c9212011-02-23 18:56:55 +00001018}
1019#endif
1020
reed@google.com5c3d1472011-02-22 19:12:23 +00001021///////////////////////////////////////////////////////////////////////////////
1022
reed@android.comba09de42010-02-05 20:46:05 +00001023void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001025 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1026 fLocalBoundsCompareTypeBW;
1027
1028 if (!this->getClipBounds(&r, et)) {
1029 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001031 rCompare.set(SkScalarToCompareType(r.fLeft),
1032 SkScalarToCompareType(r.fTop),
1033 SkScalarToCompareType(r.fRight),
1034 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 }
1036}
1037
reed@android.comd252db02009-04-01 18:31:44 +00001038/* current impl ignores edgetype, and relies on
1039 getLocalClipBoundsCompareType(), which always returns a value assuming
1040 antialiasing (worst case)
1041 */
reed@android.comba09de42010-02-05 20:46:05 +00001042bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001043
1044 if (!rect.hasValidCoordinates())
1045 return true;
1046
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 if (fMCRec->fRegion->isEmpty()) {
1048 return true;
1049 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050
tomhudson@google.com8d430182011-06-06 19:11:19 +00001051 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001052 SkRect dst;
1053 fMCRec->fMatrix->mapRect(&dst, rect);
1054 SkIRect idst;
1055 dst.roundOut(&idst);
1056 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1057 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001058 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001059
reed@android.coma380ae42009-07-21 01:17:02 +00001060 // for speed, do the most likely reject compares first
1061 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1062 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1063 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1064 return true;
1065 }
1066 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1067 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1068 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1069 return true;
1070 }
1071 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073}
1074
1075bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001076 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077}
1078
1079bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1080 /* current impl ignores edgetype, and relies on
1081 getLocalClipBoundsCompareType(), which always returns a value assuming
1082 antialiasing (worst case)
1083 */
1084
1085 if (fMCRec->fRegion->isEmpty()) {
1086 return true;
1087 }
reed@google.com4b226022011-01-11 18:32:13 +00001088
reed@android.comaefd2bc2009-03-30 21:02:14 +00001089 SkScalarCompareType userT = SkScalarToCompareType(top);
1090 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001091
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001093 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 if (userT >= userB) {
1095 return true;
1096 }
reed@google.com4b226022011-01-11 18:32:13 +00001097
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 // check if we are above or below the local clip bounds
1099 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1100 return userT >= clipR.fBottom || userB <= clipR.fTop;
1101}
1102
1103bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001104 SkIRect ibounds;
1105 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 return false;
1107 }
1108
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001109 SkMatrix inverse;
1110 // if we can't invert the CTM, we can't return local clip bounds
1111 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001112 if (bounds) {
1113 bounds->setEmpty();
1114 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001115 return false;
1116 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001118 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001119 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 // adjust it outwards if we are antialiasing
1121 int inset = (kAA_EdgeType == et);
1122 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1123 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 inverse.mapRect(bounds, r);
1125 }
1126 return true;
1127}
1128
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001129bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
1130 const SkRegion& clip = *fMCRec->fRegion;
1131 if (clip.isEmpty()) {
1132 if (bounds) {
1133 bounds->setEmpty();
1134 }
1135 return false;
1136 }
1137
1138 if (NULL != bounds) {
1139 *bounds = clip.getBounds();
1140 }
1141 return true;
1142}
1143
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144const SkMatrix& SkCanvas::getTotalMatrix() const {
1145 return *fMCRec->fMatrix;
1146}
1147
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001148SkCanvas::ClipType SkCanvas::getClipType() const {
1149 if (fMCRec->fRegion->isEmpty()) return kEmpty_ClipType;
1150 if (fMCRec->fRegion->isRect()) return kRect_ClipType;
1151 return kComplex_ClipType;
1152}
1153
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154const SkRegion& SkCanvas::getTotalClip() const {
1155 return *fMCRec->fRegion;
1156}
1157
reed@google.com7d7ca792011-02-23 22:39:18 +00001158const SkClipStack& SkCanvas::getTotalClipStack() const {
1159 return fClipStack;
1160}
1161
reed@android.comf2b98d62010-12-20 18:26:13 +00001162void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1163 if (NULL == matrix || matrix->isIdentity()) {
1164 if (fUseExternalMatrix) {
1165 fDeviceCMDirty = true;
1166 }
1167 fUseExternalMatrix = false;
1168 } else {
1169 fUseExternalMatrix = true;
1170 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001171
reed@android.comf2b98d62010-12-20 18:26:13 +00001172 fExternalMatrix = *matrix;
1173 matrix->invert(&fExternalInverse);
1174 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001175}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176
bsalomon@google.come97f0852011-06-17 13:10:25 +00001177SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1178 int width, int height,
1179 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001180 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001181 if (device) {
1182 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1183 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001184 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001185 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001186 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187}
1188
bsalomon@google.come97f0852011-06-17 13:10:25 +00001189SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1190 int width, int height,
1191 bool isOpaque) {
1192 SkDevice* device = this->getDevice();
1193 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001194 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001195 } else {
1196 return NULL;
1197 }
1198}
1199
1200
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201//////////////////////////////////////////////////////////////////////////////
1202// These are the virtual drawing methods
1203//////////////////////////////////////////////////////////////////////////////
1204
reed@google.com2a981812011-04-14 18:59:28 +00001205void SkCanvas::clear(SkColor color) {
1206 SkDrawIter iter(this);
1207
1208 while (iter.next()) {
1209 iter.fDevice->clear(color);
1210 }
1211}
1212
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001214 this->internalDrawPaint(paint);
1215}
1216
1217void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001218 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219
1220 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001221 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222 }
1223
reed@google.com4e2b3d32011-04-07 14:18:59 +00001224 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225}
1226
1227void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1228 const SkPaint& paint) {
1229 if ((long)count <= 0) {
1230 return;
1231 }
1232
1233 SkASSERT(pts != NULL);
1234
reed@google.com4e2b3d32011-04-07 14:18:59 +00001235 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001236
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001238 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 }
reed@google.com4b226022011-01-11 18:32:13 +00001240
reed@google.com4e2b3d32011-04-07 14:18:59 +00001241 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242}
1243
1244void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1245 if (paint.canComputeFastBounds()) {
1246 SkRect storage;
1247 if (this->quickReject(paint.computeFastBounds(r, &storage),
1248 paint2EdgeType(&paint))) {
1249 return;
1250 }
1251 }
reed@google.com4b226022011-01-11 18:32:13 +00001252
reed@google.com4e2b3d32011-04-07 14:18:59 +00001253 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254
1255 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001256 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257 }
1258
reed@google.com4e2b3d32011-04-07 14:18:59 +00001259 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260}
1261
1262void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001263 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001264 SkRect storage;
1265 const SkRect& bounds = path.getBounds();
1266 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 paint2EdgeType(&paint))) {
1268 return;
1269 }
1270 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001271 if (path.isEmpty()) {
1272 if (path.isInverseFillType()) {
1273 this->internalDrawPaint(paint);
1274 }
1275 return;
1276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277
reed@google.com4e2b3d32011-04-07 14:18:59 +00001278 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
1280 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001281 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 }
1283
reed@google.com4e2b3d32011-04-07 14:18:59 +00001284 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
1287void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1288 const SkPaint* paint) {
1289 SkDEBUGCODE(bitmap.validate();)
1290
1291 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1292 SkRect fastBounds;
1293 fastBounds.set(x, y,
1294 x + SkIntToScalar(bitmap.width()),
1295 y + SkIntToScalar(bitmap.height()));
1296 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1297 return;
1298 }
1299 }
reed@google.com4b226022011-01-11 18:32:13 +00001300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 SkMatrix matrix;
1302 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001303 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304}
1305
reed@google.com9987ec32011-09-07 11:57:52 +00001306// this one is non-virtual, so it can be called safely by other canvas apis
1307void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1308 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1310 return;
1311 }
reed@google.com9987ec32011-09-07 11:57:52 +00001312
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 // do this now, to avoid the cost of calling extract for RLE bitmaps
1314 if (this->quickReject(dst, paint2EdgeType(paint))) {
1315 return;
1316 }
reed@google.com9987ec32011-09-07 11:57:52 +00001317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001319
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001321 SkRect tmpSrc;
1322 if (src) {
1323 tmpSrc.set(*src);
1324 // if the extract process clipped off the top or left of the
1325 // original, we adjust for that here to get the position right.
1326 if (tmpSrc.fLeft > 0) {
1327 tmpSrc.fRight -= tmpSrc.fLeft;
1328 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001329 }
reed@android.com87899992009-10-16 14:48:38 +00001330 if (tmpSrc.fTop > 0) {
1331 tmpSrc.fBottom -= tmpSrc.fTop;
1332 tmpSrc.fTop = 0;
1333 }
1334 } else {
1335 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1336 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 }
reed@android.com87899992009-10-16 14:48:38 +00001338 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001339
reed@android.comf2b98d62010-12-20 18:26:13 +00001340 // ensure that src is "valid" before we pass it to our internal routines
1341 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1342 SkIRect tmpISrc;
1343 if (src) {
1344 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001345 if (!tmpISrc.intersect(*src)) {
1346 return;
1347 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001348 src = &tmpISrc;
1349 }
1350 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351}
1352
reed@google.com9987ec32011-09-07 11:57:52 +00001353void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1354 const SkRect& dst, const SkPaint* paint) {
1355 SkDEBUGCODE(bitmap.validate();)
1356 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1357}
1358
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1360 const SkPaint* paint) {
1361 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001362 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363}
1364
reed@android.comf2b98d62010-12-20 18:26:13 +00001365void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1366 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001368
reed@google.com4e2b3d32011-04-07 14:18:59 +00001369 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001370
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001372 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 }
reed@android.com9b039062009-02-11 15:09:58 +00001374
reed@google.com4e2b3d32011-04-07 14:18:59 +00001375 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376}
1377
reed@google.com9987ec32011-09-07 11:57:52 +00001378void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1379 const SkIRect& center, const SkRect& dst,
1380 const SkPaint* paint) {
1381 const int32_t w = bitmap.width();
1382 const int32_t h = bitmap.height();
1383
1384 SkIRect c = center;
1385 // pin center to the bounds of the bitmap
1386 c.fLeft = SkMax32(0, center.fLeft);
1387 c.fTop = SkMax32(0, center.fTop);
1388 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1389 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1390
1391 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1392 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1393 SkScalar dstX[4] = {
1394 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1395 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1396 };
1397 SkScalar dstY[4] = {
1398 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1399 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1400 };
1401
1402 if (dstX[1] > dstX[2]) {
1403 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1404 dstX[2] = dstX[1];
1405 }
1406
1407 if (dstY[1] > dstY[2]) {
1408 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1409 dstY[2] = dstY[1];
1410 }
1411
1412 SkIRect s;
1413 SkRect d;
1414 for (int y = 0; y < 3; y++) {
1415 s.fTop = srcY[y];
1416 s.fBottom = srcY[y+1];
1417 d.fTop = dstY[y];
1418 d.fBottom = dstY[y+1];
1419 for (int x = 0; x < 3; x++) {
1420 s.fLeft = srcX[x];
1421 s.fRight = srcX[x+1];
1422 d.fLeft = dstX[x];
1423 d.fRight = dstX[x+1];
1424 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1425 }
1426 }
1427}
1428
1429void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1430 const SkRect& dst, const SkPaint* paint) {
1431 SkDEBUGCODE(bitmap.validate();)
1432
1433 // Need a device entry-point, so gpu can use a mesh
1434 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1435}
1436
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1438 const SkPaint* paint) {
1439 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001440
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 if (reject_bitmap(bitmap)) {
1442 return;
1443 }
reed@google.com4b226022011-01-11 18:32:13 +00001444
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 SkPaint tmp;
1446 if (NULL == paint) {
1447 paint = &tmp;
1448 }
reed@google.com4b226022011-01-11 18:32:13 +00001449
reed@google.com4e2b3d32011-04-07 14:18:59 +00001450 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001451
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 while (iter.next()) {
1453 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001454 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001456 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457}
1458
reed@google.comf67e4cf2011-03-15 20:56:58 +00001459class SkDeviceFilteredPaint {
1460public:
1461 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1462 SkDevice::TextFlags flags;
1463 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001464 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001465 newPaint->setFlags(flags.fFlags);
1466 newPaint->setHinting(flags.fHinting);
1467 fPaint = newPaint;
1468 } else {
1469 fPaint = &paint;
1470 }
1471 }
1472
reed@google.comf67e4cf2011-03-15 20:56:58 +00001473 const SkPaint& paint() const { return *fPaint; }
1474
1475private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001476 const SkPaint* fPaint;
1477 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001478};
1479
bungeman@google.com52c748b2011-08-22 21:30:43 +00001480void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1481 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001482 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001483 draw.fDevice->drawRect(draw, r, paint);
1484 } else {
1485 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001486 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001487 draw.fDevice->drawRect(draw, r, p);
1488 }
1489}
1490
1491void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1492 const char text[], size_t byteLength,
1493 SkScalar x, SkScalar y) {
1494 SkASSERT(byteLength == 0 || text != NULL);
1495
1496 // nothing to draw
1497 if (text == NULL || byteLength == 0 ||
1498 draw.fClip->isEmpty() ||
1499 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1500 return;
1501 }
1502
1503 SkScalar width = 0;
1504 SkPoint start;
1505
1506 start.set(0, 0); // to avoid warning
1507 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1508 SkPaint::kStrikeThruText_Flag)) {
1509 width = paint.measureText(text, byteLength);
1510
1511 SkScalar offsetX = 0;
1512 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1513 offsetX = SkScalarHalf(width);
1514 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1515 offsetX = width;
1516 }
1517 start.set(x - offsetX, y);
1518 }
1519
1520 if (0 == width) {
1521 return;
1522 }
1523
1524 uint32_t flags = paint.getFlags();
1525
1526 if (flags & (SkPaint::kUnderlineText_Flag |
1527 SkPaint::kStrikeThruText_Flag)) {
1528 SkScalar textSize = paint.getTextSize();
1529 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1530 SkRect r;
1531
1532 r.fLeft = start.fX;
1533 r.fRight = start.fX + width;
1534
1535 if (flags & SkPaint::kUnderlineText_Flag) {
1536 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1537 start.fY);
1538 r.fTop = offset;
1539 r.fBottom = offset + height;
1540 DrawRect(draw, paint, r, textSize);
1541 }
1542 if (flags & SkPaint::kStrikeThruText_Flag) {
1543 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1544 start.fY);
1545 r.fTop = offset;
1546 r.fBottom = offset + height;
1547 DrawRect(draw, paint, r, textSize);
1548 }
1549 }
1550}
1551
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552void SkCanvas::drawText(const void* text, size_t byteLength,
1553 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001554 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555
1556 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001557 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001558 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001559 DrawTextDecorations(iter, dfp.paint(),
1560 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561 }
1562
reed@google.com4e2b3d32011-04-07 14:18:59 +00001563 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564}
1565
1566void SkCanvas::drawPosText(const void* text, size_t byteLength,
1567 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001568 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001569
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001571 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001572 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001573 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574 }
reed@google.com4b226022011-01-11 18:32:13 +00001575
reed@google.com4e2b3d32011-04-07 14:18:59 +00001576 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577}
1578
1579void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1580 const SkScalar xpos[], SkScalar constY,
1581 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001582 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001583
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001585 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001587 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 }
reed@google.com4b226022011-01-11 18:32:13 +00001589
reed@google.com4e2b3d32011-04-07 14:18:59 +00001590 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591}
1592
1593void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1594 const SkPath& path, const SkMatrix* matrix,
1595 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001596 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597
1598 while (iter.next()) {
1599 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001600 matrix, 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
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001606#ifdef ANDROID
1607void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1608 const SkPoint pos[], const SkPaint& paint,
1609 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001610 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001611
1612 while (iter.next()) {
1613 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001614 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001615 }
1616
reed@google.com4e2b3d32011-04-07 14:18:59 +00001617 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001618}
1619#endif
1620
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1622 const SkPoint verts[], const SkPoint texs[],
1623 const SkColor colors[], SkXfermode* xmode,
1624 const uint16_t indices[], int indexCount,
1625 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001626 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001627
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628 while (iter.next()) {
1629 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001630 colors, xmode, indices, indexCount,
1631 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 }
reed@google.com4b226022011-01-11 18:32:13 +00001633
reed@google.com4e2b3d32011-04-07 14:18:59 +00001634 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635}
1636
reed@android.comcb608442009-12-04 21:32:27 +00001637void SkCanvas::drawData(const void* data, size_t length) {
1638 // do nothing. Subclasses may do something with the data
1639}
1640
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641//////////////////////////////////////////////////////////////////////////////
1642// These methods are NOT virtual, and therefore must call back into virtual
1643// methods, rather than actually drawing themselves.
1644//////////////////////////////////////////////////////////////////////////////
1645
1646void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001647 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648 SkPaint paint;
1649
1650 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001651 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001652 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653 }
1654 this->drawPaint(paint);
1655}
1656
reed@android.com845fdac2009-06-23 03:01:32 +00001657void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658 SkPaint paint;
1659
1660 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001661 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001662 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663 }
1664 this->drawPaint(paint);
1665}
1666
1667void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1668 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001669
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670 pt.set(x, y);
1671 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1672}
1673
1674void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1675 SkPoint pt;
1676 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001677
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678 pt.set(x, y);
1679 paint.setColor(color);
1680 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1681}
1682
1683void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1684 const SkPaint& paint) {
1685 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001686
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687 pts[0].set(x0, y0);
1688 pts[1].set(x1, y1);
1689 this->drawPoints(kLines_PointMode, 2, pts, paint);
1690}
1691
1692void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1693 SkScalar right, SkScalar bottom,
1694 const SkPaint& paint) {
1695 SkRect r;
1696
1697 r.set(left, top, right, bottom);
1698 this->drawRect(r, paint);
1699}
1700
1701void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1702 const SkPaint& paint) {
1703 if (radius < 0) {
1704 radius = 0;
1705 }
1706
1707 SkRect r;
1708 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001709
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710 if (paint.canComputeFastBounds()) {
1711 SkRect storage;
1712 if (this->quickReject(paint.computeFastBounds(r, &storage),
1713 paint2EdgeType(&paint))) {
1714 return;
1715 }
1716 }
reed@google.com4b226022011-01-11 18:32:13 +00001717
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718 SkPath path;
1719 path.addOval(r);
1720 this->drawPath(path, paint);
1721}
1722
1723void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1724 const SkPaint& paint) {
1725 if (rx > 0 && ry > 0) {
1726 if (paint.canComputeFastBounds()) {
1727 SkRect storage;
1728 if (this->quickReject(paint.computeFastBounds(r, &storage),
1729 paint2EdgeType(&paint))) {
1730 return;
1731 }
1732 }
1733
1734 SkPath path;
1735 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1736 this->drawPath(path, paint);
1737 } else {
1738 this->drawRect(r, paint);
1739 }
1740}
1741
1742void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1743 if (paint.canComputeFastBounds()) {
1744 SkRect storage;
1745 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1746 paint2EdgeType(&paint))) {
1747 return;
1748 }
1749 }
1750
1751 SkPath path;
1752 path.addOval(oval);
1753 this->drawPath(path, paint);
1754}
1755
1756void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1757 SkScalar sweepAngle, bool useCenter,
1758 const SkPaint& paint) {
1759 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1760 this->drawOval(oval, paint);
1761 } else {
1762 SkPath path;
1763 if (useCenter) {
1764 path.moveTo(oval.centerX(), oval.centerY());
1765 }
1766 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1767 if (useCenter) {
1768 path.close();
1769 }
1770 this->drawPath(path, paint);
1771 }
1772}
1773
1774void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1775 const SkPath& path, SkScalar hOffset,
1776 SkScalar vOffset, const SkPaint& paint) {
1777 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001778
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 matrix.setTranslate(hOffset, vOffset);
1780 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1781}
1782
reed@android.comf76bacf2009-05-13 14:00:33 +00001783///////////////////////////////////////////////////////////////////////////////
1784
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785void SkCanvas::drawPicture(SkPicture& picture) {
1786 int saveCount = save();
1787 picture.draw(this);
1788 restoreToCount(saveCount);
1789}
1790
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791///////////////////////////////////////////////////////////////////////////////
1792///////////////////////////////////////////////////////////////////////////////
1793
1794SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001795 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796
1797 SkASSERT(canvas);
1798
1799 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1800 fDone = !fImpl->next();
1801}
1802
1803SkCanvas::LayerIter::~LayerIter() {
1804 fImpl->~SkDrawIter();
1805}
1806
1807void SkCanvas::LayerIter::next() {
1808 fDone = !fImpl->next();
1809}
1810
1811SkDevice* SkCanvas::LayerIter::device() const {
1812 return fImpl->getDevice();
1813}
1814
1815const SkMatrix& SkCanvas::LayerIter::matrix() const {
1816 return fImpl->getMatrix();
1817}
1818
1819const SkPaint& SkCanvas::LayerIter::paint() const {
1820 const SkPaint* paint = fImpl->getPaint();
1821 if (NULL == paint) {
1822 paint = &fDefaultPaint;
1823 }
1824 return *paint;
1825}
1826
1827const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1828int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1829int SkCanvas::LayerIter::y() const { return fImpl->getY(); }