blob: d200ba3088cc24f5cb07fd111f44eb93e6e165f9 [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"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
reed@google.com82ce2b82012-06-26 17:43:26 +000029SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000030SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000031SK_DEFINE_INST_COUNT(SkDrawFilter)
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
94class AutoCheckNoSetContext {
95public:
96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
97 this->assertNoSetContext(fPaint);
98 }
99 ~AutoCheckNoSetContext() {
100 this->assertNoSetContext(fPaint);
101 }
102
103private:
104 const SkPaint& fPaint;
105
106 void assertNoSetContext(const SkPaint& paint) {
107 SkShader* s = paint.getShader();
108 if (s) {
109 SkASSERT(!s->setContextHasBeenCalled());
110 }
111 }
112};
113
114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
119 #define CHECK_SHADER_NOSETCONTEXT(paint)
120#endif
121
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000122typedef SkTLazy<SkPaint> SkLazyPaint;
123
reed@google.com97af1a62012-08-28 12:19:02 +0000124void SkCanvas::predrawNotify() {
125 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000127 }
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
132/* This is the record we keep for each SkDevice that the user installs.
133 The clip/matrix/proc are fields that reflect the top of the save/restore
134 stack. Whenever the canvas changes, it marks a dirty flag, and then before
135 these are used (assuming we're not on a layer) we rebuild these cache
136 values: they reflect the top of the save stack, but translated and clipped
137 by the device's XY offset and bitmap-bounds.
138*/
139struct DeviceCM {
140 DeviceCM* fNext;
141 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000142 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000146 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 : fNext(NULL) {
148 if (NULL != device) {
149 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000150 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@google.com4b226022011-01-11 18:32:13 +0000152 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000156 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000158 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fDevice->unref();
160 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000161 SkDELETE(fPaint);
162 }
reed@google.com4b226022011-01-11 18:32:13 +0000163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
165 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000166 int x = fDevice->getOrigin().x();
167 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 int width = fDevice->width();
169 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if ((x | y) == 0) {
172 fMatrix = &totalMatrix;
173 fClip = totalClip;
174 } else {
175 fMatrixStorage = totalMatrix;
176 fMatrixStorage.postTranslate(SkIntToScalar(-x),
177 SkIntToScalar(-y));
178 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 totalClip.translate(-x, -y, &fClip);
181 }
182
reed@google.com045e62d2011-10-24 12:19:46 +0000183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000186
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkRegion::kDifference_Op);
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#ifdef SK_DEBUG
195 if (!fClip.isEmpty()) {
196 SkIRect deviceR;
197 deviceR.set(0, 0, width, height);
198 SkASSERT(deviceR.contains(fClip.getBounds()));
199 }
200#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000201 }
202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000204 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207/* This is the record we keep for each save/restore level in the stack.
208 Since a level optionally copies the matrix and/or stack, we have pointers
209 for these fields. If the value is copied for this level, the copy is
210 stored in the ...Storage field, and the pointer points to that. If the
211 value is not copied for this level, we ignore ...Storage, and just point
212 at the corresponding value in the previous level in the stack.
213*/
214class SkCanvas::MCRec {
215public:
216 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
219 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 DeviceCM* fLayer;
222 /* If there are any layers in the stack, this points to the top-most
223 one that is at or below this level in the stack (so we know what
224 bitmap/device to draw into from this level. This value is NOT
225 reference counted, since the real owner is either our fLayer field,
226 or a previous one in a lower level.)
227 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000228 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
230 MCRec(const MCRec* prev, int flags) {
231 if (NULL != prev) {
232 if (flags & SkCanvas::kMatrix_SaveFlag) {
233 fMatrixStorage = *prev->fMatrix;
234 fMatrix = &fMatrixStorage;
235 } else {
236 fMatrix = prev->fMatrix;
237 }
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000240 fRasterClipStorage = *prev->fRasterClip;
241 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 } else {
reed@google.com00177082011-10-12 14:34:30 +0000243 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
245
246 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000247 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 fTopLayer = prev->fTopLayer;
250 } else { // no prev
251 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000254 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = NULL;
256 fTopLayer = NULL;
257 }
258 fLayer = NULL;
259
260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000264 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDELETE(fLayer);
266 dec_rec();
267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
reed@google.com00177082011-10-12 14:34:30 +0000270 SkMatrix fMatrixStorage;
271 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
274class SkDrawIter : public SkDraw {
275public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000277 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000278 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 canvas->updateDeviceCMCache();
280
reed@google.com90c07ea2012-04-13 13:50:27 +0000281 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fBounder = canvas->getBounder();
283 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000284 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com4b226022011-01-11 18:32:13 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 bool next() {
288 // skip over recs with empty clips
289 if (fSkipEmptyClips) {
290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
291 fCurrLayer = fCurrLayer->fNext;
292 }
293 }
294
reed@google.comf68c5e22012-02-24 16:38:58 +0000295 const DeviceCM* rec = fCurrLayer;
296 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297
298 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
300 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 fDevice = rec->fDevice;
302 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000304 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 fCurrLayer = rec->fNext;
307 if (fBounder) {
308 fBounder->setClip(fClip);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313 }
314 return false;
315 }
reed@google.com4b226022011-01-11 18:32:13 +0000316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000318 int getX() const { return fDevice->getOrigin().x(); }
319 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 const SkMatrix& getMatrix() const { return *fMatrix; }
321 const SkRegion& getClip() const { return *fClip; }
322 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324private:
325 SkCanvas* fCanvas;
326 const DeviceCM* fCurrLayer;
327 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 SkBool8 fSkipEmptyClips;
329
330 typedef SkDraw INHERITED;
331};
332
333/////////////////////////////////////////////////////////////////////////////
334
335class AutoDrawLooper {
336public:
reed@google.com8926b162012-03-23 15:36:36 +0000337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
338 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000339 fCanvas = canvas;
340 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000344 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
reed@google.com8926b162012-03-23 15:36:36 +0000347 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
348 SkPaint tmp;
349 tmp.setImageFilter(fOrigPaint.getImageFilter());
350 // it would be nice if we had a guess at the bounds, instead of null
351 (void)canvas->internalSaveLayer(NULL, &tmp,
352 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
353 // we'll clear the imageFilter for the actual draws in next(), so
354 // it will only be applied during the restore().
355 fDoClearImageFilter = true;
356 }
357
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 if (fLooper) {
359 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000360 fIsSimple = false;
361 } else {
362 // can we be marked as simple?
363 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364 }
365 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000368 if (fDoClearImageFilter) {
369 fCanvas->internalRestore();
370 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000371 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000373
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 const SkPaint& paint() const {
375 SkASSERT(fPaint);
376 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000378
reed@google.com129ec222012-05-15 13:24:09 +0000379 bool next(SkDrawFilter::Type drawType) {
380 if (fDone) {
381 return false;
382 } else if (fIsSimple) {
383 fDone = true;
384 fPaint = &fOrigPaint;
385 return !fPaint->nothingToDraw();
386 } else {
387 return this->doNext(drawType);
388 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000389 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000392 SkLazyPaint fLazyPaint;
393 SkCanvas* fCanvas;
394 const SkPaint& fOrigPaint;
395 SkDrawLooper* fLooper;
396 SkDrawFilter* fFilter;
397 const SkPaint* fPaint;
398 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000399 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000400 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000401 bool fIsSimple;
402
403 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404};
405
reed@google.com129ec222012-05-15 13:24:09 +0000406bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000407 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000408 SkASSERT(!fIsSimple);
409 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
410
411 SkPaint* paint = fLazyPaint.set(fOrigPaint);
412
413 if (fDoClearImageFilter) {
414 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416
reed@google.com129ec222012-05-15 13:24:09 +0000417 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000419 return false;
420 }
421 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000422 if (!fFilter->filter(paint, drawType)) {
423 fDone = true;
424 return false;
425 }
reed@google.com129ec222012-05-15 13:24:09 +0000426 if (NULL == fLooper) {
427 // no looper means we only draw once
428 fDone = true;
429 }
430 }
431 fPaint = paint;
432
433 // if we only came in here for the imagefilter, mark us as done
434 if (!fLooper && !fFilter) {
435 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000436 }
437
438 // call this after any possible paint modifiers
439 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440 fPaint = NULL;
441 return false;
442 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 return true;
444}
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446/* Stack helper for managing a SkBounder. In the destructor, if we were
447 given a bounder, we call its commit() method, signifying that we are
448 done accumulating bounds for that draw.
449*/
450class SkAutoBounderCommit {
451public:
452 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
453 ~SkAutoBounderCommit() {
454 if (NULL != fBounder) {
455 fBounder->commit();
456 }
457 }
458private:
459 SkBounder* fBounder;
460};
461
462#include "SkColorPriv.h"
463
464class AutoValidator {
465public:
466 AutoValidator(SkDevice* device) : fDevice(device) {}
467 ~AutoValidator() {
468#ifdef SK_DEBUG
469 const SkBitmap& bm = fDevice->accessBitmap(false);
470 if (bm.config() == SkBitmap::kARGB_4444_Config) {
471 for (int y = 0; y < bm.height(); y++) {
472 const SkPMColor16* p = bm.getAddr16(0, y);
473 for (int x = 0; x < bm.width(); x++) {
474 SkPMColor16 c = p[x];
475 SkPMColor16Assert(c);
476 }
477 }
478 }
479#endif
480 }
481private:
482 SkDevice* fDevice;
483};
484
485////////// macros to place around the internal draw calls //////////////////
486
reed@google.com8926b162012-03-23 15:36:36 +0000487#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
488/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000489 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000490 AutoDrawLooper looper(this, paint, true); \
491 while (looper.next(type)) { \
492 SkAutoBounderCommit ac(fBounder); \
493 SkDrawIter iter(this);
494
reed@google.com4e2b3d32011-04-07 14:18:59 +0000495#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000497 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000498 AutoDrawLooper looper(this, paint); \
499 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 SkAutoBounderCommit ac(fBounder); \
501 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000502
reed@google.com4e2b3d32011-04-07 14:18:59 +0000503#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
505////////////////////////////////////////////////////////////////////////////
506
507SkDevice* SkCanvas::init(SkDevice* device) {
508 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000509 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000511 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000512 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000513 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000514 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000515 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516
517 fMCRec = (MCRec*)fMCStack.push_back();
518 new (fMCRec) MCRec(NULL, 0);
519
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000520 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 fMCRec->fTopLayer = fMCRec->fLayer;
522 fMCRec->fNext = NULL;
523
reed@google.com97af1a62012-08-28 12:19:02 +0000524 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000525
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 return this->setDevice(device);
527}
528
reed@google.comcde92112011-07-06 20:00:52 +0000529SkCanvas::SkCanvas()
530: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000531 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000532
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000533 this->init(NULL);
534}
535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000537 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 inc_canvas();
539
540 this->init(device);
541}
542
543SkCanvas::SkCanvas(const SkBitmap& bitmap)
544 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
545 inc_canvas();
546
reed@google.comcde92112011-07-06 20:00:52 +0000547 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548}
549
550SkCanvas::~SkCanvas() {
551 // free up the contents of our deque
552 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000553 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000554
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 this->internalRestore(); // restore the last, since we're going away
556
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000557 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000558 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 dec_canvas();
561}
562
563SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
564 SkRefCnt_SafeAssign(fBounder, bounder);
565 return bounder;
566}
567
568SkDrawFilter* SkCanvas::getDrawFilter() const {
569 return fMCRec->fFilter;
570}
571
572SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
573 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
574 return filter;
575}
576
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000577SkMetaData& SkCanvas::getMetaData() {
578 // metadata users are rare, so we lazily allocate it. If that changes we
579 // can decide to just make it a field in the device (rather than a ptr)
580 if (NULL == fMetaData) {
581 fMetaData = new SkMetaData;
582 }
583 return *fMetaData;
584}
585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586///////////////////////////////////////////////////////////////////////////////
587
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000588void SkCanvas::flush() {
589 SkDevice* device = this->getDevice();
590 if (device) {
591 device->flush();
592 }
593}
594
reed@google.com210ce002011-11-01 14:24:23 +0000595SkISize SkCanvas::getDeviceSize() const {
596 SkDevice* d = this->getDevice();
597 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
598}
599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600SkDevice* SkCanvas::getDevice() const {
601 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000602 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 SkASSERT(rec && rec->fLayer);
604 return rec->fLayer->fDevice;
605}
606
reed@google.com0b53d592012-03-19 18:26:34 +0000607SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
608 if (updateMatrixClip) {
609 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
610 }
reed@google.com9266fed2011-03-30 00:18:03 +0000611 return fMCRec->fTopLayer->fDevice;
612}
613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614SkDevice* SkCanvas::setDevice(SkDevice* device) {
615 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000616 SkDeque::F2BIter iter(fMCStack);
617 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 SkASSERT(rec && rec->fLayer);
619 SkDevice* rootDevice = rec->fLayer->fDevice;
620
621 if (rootDevice == device) {
622 return device;
623 }
reed@google.com4b226022011-01-11 18:32:13 +0000624
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000626 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 }
628 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000629 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 }
631
632 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
633 rootDevice = device;
634
635 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 /* Now we update our initial region to have the bounds of the new device,
638 and then intersect all of the clips in our stack with these bounds,
639 to ensure that we can't draw outside of the device's bounds (and trash
640 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 NOTE: this is only a partial-fix, since if the new device is larger than
643 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000644 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
646 reconstruct the correct clips, so this approximation will have to do.
647 The caller really needs to restore() back to the base if they want to
648 accurately take advantage of the new device bounds.
649 */
650
reed@google.com42aea282012-03-28 16:19:15 +0000651 SkIRect bounds;
652 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000654 } else {
655 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 }
reed@google.com42aea282012-03-28 16:19:15 +0000657 // now jam our 1st clip to be bounds, and intersect the rest with that
658 rec->fRasterClip->setRect(bounds);
659 while ((rec = (MCRec*)iter.next()) != NULL) {
660 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
661 }
662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 return device;
664}
665
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000666bool SkCanvas::readPixels(SkBitmap* bitmap,
667 int x, int y,
668 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000669 SkDevice* device = this->getDevice();
670 if (!device) {
671 return false;
672 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000673 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000674}
675
bsalomon@google.comc6980972011-11-02 19:57:21 +0000676bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000677 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000678 if (!device) {
679 return false;
680 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000681
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000682 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000683 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000684 if (!bounds.intersect(srcRect)) {
685 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000686 }
687
688 SkBitmap tmp;
689 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
690 bounds.height());
691 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
692 bitmap->swap(tmp);
693 return true;
694 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000695 return false;
696 }
reed@google.com51df9e32010-12-23 19:29:18 +0000697}
698
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000699void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
700 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000701 SkDevice* device = this->getDevice();
702 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000703 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
704 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
705 device->accessBitmap(true);
706 device->writePixels(bitmap, x, y, config8888);
707 }
reed@google.com51df9e32010-12-23 19:29:18 +0000708 }
709}
710
junov@google.com4370aed2012-01-18 16:21:08 +0000711SkCanvas* SkCanvas::canvasForDrawIter() {
712 return this;
713}
714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715//////////////////////////////////////////////////////////////////////////////
716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717void SkCanvas::updateDeviceCMCache() {
718 if (fDeviceCMDirty) {
719 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000720 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000722
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000724 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000726 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000728 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 } while ((layer = layer->fNext) != NULL);
730 }
731 fDeviceCMDirty = false;
732 }
733}
734
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735///////////////////////////////////////////////////////////////////////////////
736
737int SkCanvas::internalSave(SaveFlags flags) {
738 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000739
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 MCRec* newTop = (MCRec*)fMCStack.push_back();
741 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000742
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 newTop->fNext = fMCRec;
744 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000745
reed@google.com5c3d1472011-02-22 19:12:23 +0000746 fClipStack.save();
747 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
748
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 return saveCount;
750}
751
752int SkCanvas::save(SaveFlags flags) {
753 // call shared impl
754 return this->internalSave(flags);
755}
756
757#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
758#define C16MASK (1 << SkBitmap::kRGB_565_Config)
759#define C8MASK (1 << SkBitmap::kA8_Config)
760
761static SkBitmap::Config resolve_config(SkCanvas* canvas,
762 const SkIRect& bounds,
763 SkCanvas::SaveFlags flags,
764 bool* isOpaque) {
765 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
766
767#if 0
768 // loop through and union all the configs we may draw into
769 uint32_t configMask = 0;
770 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
771 {
772 SkDevice* device = canvas->getLayerDevice(i);
773 if (device->intersects(bounds))
774 configMask |= 1 << device->config();
775 }
776
777 // if the caller wants alpha or fullcolor, we can't return 565
778 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
779 SkCanvas::kHasAlphaLayer_SaveFlag))
780 configMask &= ~C16MASK;
781
782 switch (configMask) {
783 case C8MASK: // if we only have A8, return that
784 return SkBitmap::kA8_Config;
785
786 case C16MASK: // if we only have 565, return that
787 return SkBitmap::kRGB_565_Config;
788
789 default:
790 return SkBitmap::kARGB_8888_Config; // default answer
791 }
792#else
793 return SkBitmap::kARGB_8888_Config; // default answer
794#endif
795}
796
797static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
798 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
799}
800
junov@chromium.orga907ac32012-02-24 21:54:07 +0000801bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
802 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000803 SkIRect clipBounds;
804 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000805 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000806 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000807 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 if (NULL != bounds) {
809 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000810
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 this->getTotalMatrix().mapRect(&r, *bounds);
812 r.roundOut(&ir);
813 // early exit if the layer's bounds are clipped out
814 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000815 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000816 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000817 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000818 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 }
820 } else { // no user bounds, so just use the clip
821 ir = clipBounds;
822 }
823
reed@google.com5c3d1472011-02-22 19:12:23 +0000824 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 // early exit if the clip is now empty
827 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000828 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000829 return false;
830 }
831
832 if (intersection) {
833 *intersection = ir;
834 }
835 return true;
836}
837
838int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
839 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000840 return this->internalSaveLayer(bounds, paint, flags, false);
841}
842
843int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
844 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000845 // do this before we create the layer. We don't call the public save() since
846 // that would invoke a possibly overridden virtual
847 int count = this->internalSave(flags);
848
849 fDeviceCMDirty = true;
850
851 SkIRect ir;
852 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 return count;
854 }
855
reed@google.comb55deeb2012-01-06 14:43:09 +0000856 // Kill the imagefilter if our device doesn't allow it
857 SkLazyPaint lazyP;
858 if (paint && paint->getImageFilter()) {
859 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000860 if (justForImageFilter) {
861 // early exit if the layer was just for the imageFilter
862 return count;
863 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000864 SkPaint* p = lazyP.set(*paint);
865 p->setImageFilter(NULL);
866 paint = p;
867 }
868 }
869
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 bool isOpaque;
871 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
872
reed@google.com76dd2772012-01-05 21:15:07 +0000873 SkDevice* device;
874 if (paint && paint->getImageFilter()) {
875 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
876 isOpaque);
877 } else {
878 device = this->createLayerDevice(config, ir.width(), ir.height(),
879 isOpaque);
880 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000881 if (NULL == device) {
882 SkDebugf("Unable to create device for layer.");
883 return count;
884 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000885
reed@google.com6f8f2922011-03-04 22:27:10 +0000886 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000887 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 device->unref();
889
890 layer->fNext = fMCRec->fTopLayer;
891 fMCRec->fLayer = layer;
892 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
893
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000894 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 return count;
896}
897
898int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
899 SaveFlags flags) {
900 if (0xFF == alpha) {
901 return this->saveLayer(bounds, NULL, flags);
902 } else {
903 SkPaint tmpPaint;
904 tmpPaint.setAlpha(alpha);
905 return this->saveLayer(bounds, &tmpPaint, flags);
906 }
907}
908
909void SkCanvas::restore() {
910 // check for underflow
911 if (fMCStack.count() > 1) {
912 this->internalRestore();
913 }
914}
915
916void SkCanvas::internalRestore() {
917 SkASSERT(fMCStack.count() != 0);
918
919 fDeviceCMDirty = true;
920 fLocalBoundsCompareTypeDirty = true;
921
reed@google.com5c3d1472011-02-22 19:12:23 +0000922 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000923 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 DeviceCM* layer = fMCRec->fLayer; // may be null
925 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
926 fMCRec->fLayer = NULL;
927
928 // now do the normal restore()
929 fMCRec->~MCRec(); // balanced in save()
930 fMCStack.pop_back();
931 fMCRec = (MCRec*)fMCStack.back();
932
933 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
934 since if we're being recorded, we don't want to record this (the
935 recorder will have already recorded the restore).
936 */
937 if (NULL != layer) {
938 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000939 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000940 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
941 layer->fPaint);
942 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000944
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000945 SkASSERT(fSaveLayerCount > 0);
946 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 }
948 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000949 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000950
951 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952}
953
954int SkCanvas::getSaveCount() const {
955 return fMCStack.count();
956}
957
958void SkCanvas::restoreToCount(int count) {
959 // sanity check
960 if (count < 1) {
961 count = 1;
962 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000963
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000964 int n = this->getSaveCount() - count;
965 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 this->restore();
967 }
968}
969
reed@google.com7c202932011-12-14 18:48:05 +0000970bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000971 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000972}
973
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974/////////////////////////////////////////////////////////////////////////////
975
976// can't draw it if its empty, or its too big for a fixed-point width or height
977static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000978 return bitmap.width() <= 0 || bitmap.height() <= 0
979#ifndef SK_ALLOW_OVER_32K_BITMAPS
980 || bitmap.width() > 32767 || bitmap.height() > 32767
981#endif
982 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983}
984
reed@android.comf2b98d62010-12-20 18:26:13 +0000985void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 const SkMatrix& matrix, const SkPaint* paint) {
987 if (reject_bitmap(bitmap)) {
988 return;
989 }
990
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000991 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000993 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000995 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996}
997
reed@google.com8926b162012-03-23 15:36:36 +0000998void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
999 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 SkPaint tmp;
1001 if (NULL == paint) {
1002 tmp.setDither(true);
1003 paint = &tmp;
1004 }
reed@google.com4b226022011-01-11 18:32:13 +00001005
reed@google.com8926b162012-03-23 15:36:36 +00001006 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +00001008 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001009 paint = &looper.paint();
1010 SkImageFilter* filter = paint->getImageFilter();
1011 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001012 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001013 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001014 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +00001015 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +00001016 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001017 SkPaint tmpUnfiltered(*paint);
1018 tmpUnfiltered.setImageFilter(NULL);
1019 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001020 }
1021 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001022 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001023 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001025 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026}
1027
reed@google.com8926b162012-03-23 15:36:36 +00001028void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1029 const SkPaint* paint) {
1030 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001031 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001032
reed@google.com8926b162012-03-23 15:36:36 +00001033 if (reject_bitmap(bitmap)) {
1034 return;
1035 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001036
reed@google.com8926b162012-03-23 15:36:36 +00001037 SkPaint tmp;
1038 if (NULL == paint) {
1039 paint = &tmp;
1040 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001041
reed@google.com8926b162012-03-23 15:36:36 +00001042 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001043
reed@google.com8926b162012-03-23 15:36:36 +00001044 while (iter.next()) {
1045 paint = &looper.paint();
1046 SkImageFilter* filter = paint->getImageFilter();
1047 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1048 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001049 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001050 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001051 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1052 &dst, &pos)) {
1053 SkPaint tmpUnfiltered(*paint);
1054 tmpUnfiltered.setImageFilter(NULL);
1055 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1056 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001057 }
1058 } else {
1059 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1060 }
1061 }
1062 LOOPER_END
1063}
1064
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065/////////////////////////////////////////////////////////////////////////////
1066
1067bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1068 fDeviceCMDirty = true;
1069 fLocalBoundsCompareTypeDirty = true;
1070 return fMCRec->fMatrix->preTranslate(dx, dy);
1071}
1072
1073bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1074 fDeviceCMDirty = true;
1075 fLocalBoundsCompareTypeDirty = true;
1076 return fMCRec->fMatrix->preScale(sx, sy);
1077}
1078
1079bool SkCanvas::rotate(SkScalar degrees) {
1080 fDeviceCMDirty = true;
1081 fLocalBoundsCompareTypeDirty = true;
1082 return fMCRec->fMatrix->preRotate(degrees);
1083}
1084
1085bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1086 fDeviceCMDirty = true;
1087 fLocalBoundsCompareTypeDirty = true;
1088 return fMCRec->fMatrix->preSkew(sx, sy);
1089}
1090
1091bool SkCanvas::concat(const SkMatrix& matrix) {
1092 fDeviceCMDirty = true;
1093 fLocalBoundsCompareTypeDirty = true;
1094 return fMCRec->fMatrix->preConcat(matrix);
1095}
1096
1097void SkCanvas::setMatrix(const SkMatrix& matrix) {
1098 fDeviceCMDirty = true;
1099 fLocalBoundsCompareTypeDirty = true;
1100 *fMCRec->fMatrix = matrix;
1101}
1102
1103// this is not virtual, so it must call a virtual method so that subclasses
1104// will see its action
1105void SkCanvas::resetMatrix() {
1106 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001107
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 matrix.reset();
1109 this->setMatrix(matrix);
1110}
1111
1112//////////////////////////////////////////////////////////////////////////////
1113
reed@google.comc42d35d2011-10-12 11:57:42 +00001114bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001115#ifdef SK_ENABLE_CLIP_QUICKREJECT
1116 if (SkRegion::kIntersect_Op == op) {
1117 if (fMCRec->fRasterClip->isEmpty()) {
1118 return false;
1119 }
1120
reed@google.com3b3e8952012-08-16 20:53:31 +00001121 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001122 fDeviceCMDirty = true;
1123 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001124
1125 fClipStack.clipEmpty();
1126 return fMCRec->fRasterClip->setEmpty();
1127 }
1128 }
1129#endif
1130
reed@google.com5c3d1472011-02-22 19:12:23 +00001131 AutoValidateClip avc(this);
1132
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 fDeviceCMDirty = true;
1134 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001135 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136
1137 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001138 // for these simpler matrices, we can stay a rect ever after applying
1139 // the matrix. This means we don't have to a) make a path, and b) tell
1140 // the region code to scan-convert the path, only to discover that it
1141 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143
1144 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001145 fClipStack.clipDevRect(r, op, doAA);
1146 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001148 // since we're rotate or some such thing, we convert the rect to a path
1149 // and clip against that, since it can handle any matrix. However, to
1150 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1151 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 SkPath path;
1153
1154 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001155 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 }
1157}
1158
reed@google.com00177082011-10-12 14:34:30 +00001159static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001160 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001161 // base is used to limit the size (and therefore memory allocation) of the
1162 // region that results from scan converting devPath.
1163 SkRegion base;
1164
reed@google.com819c9212011-02-23 18:56:55 +00001165 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001166 // since we are intersect, we can do better (tighter) with currRgn's
1167 // bounds, than just using the device. However, if currRgn is complex,
1168 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001169 if (currClip->isRect()) {
1170 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001171 } else {
reed@google.com00177082011-10-12 14:34:30 +00001172 base.setRect(currClip->getBounds());
1173 SkRasterClip clip;
1174 clip.setPath(devPath, base, doAA);
1175 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001176 }
reed@google.com819c9212011-02-23 18:56:55 +00001177 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001178 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001179 if (!device) {
1180 return currClip->setEmpty();
1181 }
1182
junov@chromium.orga907ac32012-02-24 21:54:07 +00001183 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001184
1185 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001186 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001187 } else {
reed@google.com00177082011-10-12 14:34:30 +00001188 SkRasterClip clip;
1189 clip.setPath(devPath, base, doAA);
1190 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001191 }
1192 }
1193}
1194
reed@google.com4ed0fb72012-12-12 20:48:18 +00001195bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1196 if (rrect.isRect()) {
1197 // call the non-virtual version
1198 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1199 } else {
1200 SkPath path;
1201 path.addRRect(rrect);
1202 // call the non-virtual version
1203 return this->SkCanvas::clipPath(path, op, doAA);
1204 }
1205}
1206
reed@google.comc42d35d2011-10-12 11:57:42 +00001207bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001208#ifdef SK_ENABLE_CLIP_QUICKREJECT
1209 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1210 if (fMCRec->fRasterClip->isEmpty()) {
1211 return false;
1212 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001213
reed@google.com3b3e8952012-08-16 20:53:31 +00001214 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001215 fDeviceCMDirty = true;
1216 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001217
reed@google.comda17f752012-08-16 18:27:05 +00001218 fClipStack.clipEmpty();
1219 return fMCRec->fRasterClip->setEmpty();
1220 }
1221 }
1222#endif
1223
reed@google.com5c3d1472011-02-22 19:12:23 +00001224 AutoValidateClip avc(this);
1225
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 fDeviceCMDirty = true;
1227 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001228 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229
1230 SkPath devPath;
1231 path.transform(*fMCRec->fMatrix, &devPath);
1232
reed@google.comfe701122011-11-08 19:41:23 +00001233 // Check if the transfomation, or the original path itself
1234 // made us empty. Note this can also happen if we contained NaN
1235 // values. computing the bounds detects this, and will set our
1236 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1237 if (devPath.getBounds().isEmpty()) {
1238 // resetting the path will remove any NaN or other wanky values
1239 // that might upset our scan converter.
1240 devPath.reset();
1241 }
1242
reed@google.com5c3d1472011-02-22 19:12:23 +00001243 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001244 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001245
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001246 if (fAllowSimplifyClip) {
1247 devPath.reset();
1248 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1249 const SkClipStack* clipStack = getClipStack();
1250 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1251 const SkClipStack::Element* element;
1252 while ((element = iter.next())) {
1253 SkClipStack::Element::Type type = element->getType();
1254 if (type == SkClipStack::Element::kEmpty_Type) {
1255 continue;
1256 }
1257 SkPath operand;
1258 if (type == SkClipStack::Element::kRect_Type) {
1259 operand.addRect(element->getRect());
1260 } else if (type == SkClipStack::Element::kPath_Type) {
1261 operand = element->getPath();
1262 } else {
1263 SkDEBUGFAIL("Unexpected type.");
1264 }
1265 SkRegion::Op elementOp = element->getOp();
1266 if (elementOp == SkRegion::kReplace_Op) {
1267 devPath = operand;
1268 } else {
1269 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1270 }
1271 }
1272 op = SkRegion::kReplace_Op;
1273 }
1274
reed@google.com00177082011-10-12 14:34:30 +00001275 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276}
1277
1278bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001279 AutoValidateClip avc(this);
1280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 fDeviceCMDirty = true;
1282 fLocalBoundsCompareTypeDirty = true;
1283
reed@google.com5c3d1472011-02-22 19:12:23 +00001284 // todo: signal fClipStack that we have a region, and therefore (I guess)
1285 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001286 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001287
reed@google.com00177082011-10-12 14:34:30 +00001288 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289}
1290
reed@google.com819c9212011-02-23 18:56:55 +00001291#ifdef SK_DEBUG
1292void SkCanvas::validateClip() const {
1293 // construct clipRgn from the clipstack
1294 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001295 if (!device) {
1296 SkASSERT(this->getTotalClip().isEmpty());
1297 return;
1298 }
1299
reed@google.com819c9212011-02-23 18:56:55 +00001300 SkIRect ir;
1301 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001302 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001303
robertphillips@google.com80214e22012-07-20 15:33:18 +00001304 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001305 const SkClipStack::Element* element;
1306 while ((element = iter.next()) != NULL) {
1307 switch (element->getType()) {
1308 case SkClipStack::Element::kPath_Type:
1309 clipPathHelper(this,
1310 &tmpClip,
1311 element->getPath(),
1312 element->getOp(),
1313 element->isAA());
1314 break;
1315 case SkClipStack::Element::kRect_Type:
1316 element->getRect().round(&ir);
1317 tmpClip.op(ir, element->getOp());
1318 break;
1319 case SkClipStack::Element::kEmpty_Type:
1320 tmpClip.setEmpty();
1321 break;
reed@google.com819c9212011-02-23 18:56:55 +00001322 }
1323 }
1324
reed@google.com6f8f2922011-03-04 22:27:10 +00001325#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001326 // now compare against the current rgn
1327 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001328 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001329#endif
reed@google.com819c9212011-02-23 18:56:55 +00001330}
1331#endif
1332
reed@google.com90c07ea2012-04-13 13:50:27 +00001333void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001334 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001335 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001336
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001337 static const SkRect kEmpty = { 0, 0, 0, 0 };
1338 while ((element = iter.next()) != NULL) {
1339 switch (element->getType()) {
1340 case SkClipStack::Element::kPath_Type:
1341 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1342 break;
1343 case SkClipStack::Element::kRect_Type:
1344 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1345 break;
1346 case SkClipStack::Element::kEmpty_Type:
1347 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1348 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001349 }
1350 }
1351}
1352
reed@google.com5c3d1472011-02-22 19:12:23 +00001353///////////////////////////////////////////////////////////////////////////////
1354
reed@google.com3b3e8952012-08-16 20:53:31 +00001355void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001357
reed@google.com3b3e8952012-08-16 20:53:31 +00001358 if (!this->getClipBounds(&r)) {
1359 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001361 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1362 SkScalarToCompareType(r.fTop),
1363 SkScalarToCompareType(r.fRight),
1364 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365 }
1366}
1367
reed@google.com3b3e8952012-08-16 20:53:31 +00001368bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001369
reed@google.com16078632011-12-06 18:56:37 +00001370 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001371 return true;
1372
reed@google.com00177082011-10-12 14:34:30 +00001373 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 return true;
1375 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376
tomhudson@google.com8d430182011-06-06 19:11:19 +00001377 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001378 SkRect dst;
1379 fMCRec->fMatrix->mapRect(&dst, rect);
1380 SkIRect idst;
1381 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001382 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001383 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001384 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001385
reed@android.coma380ae42009-07-21 01:17:02 +00001386 // for speed, do the most likely reject compares first
1387 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1388 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1389 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1390 return true;
1391 }
1392 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1393 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1394 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1395 return true;
1396 }
1397 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399}
1400
reed@google.com3b3e8952012-08-16 20:53:31 +00001401bool SkCanvas::quickReject(const SkPath& path) const {
1402 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403}
1404
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001405static inline int pinIntForScalar(int x) {
1406#ifdef SK_SCALAR_IS_FIXED
1407 if (x < SK_MinS16) {
1408 x = SK_MinS16;
1409 } else if (x > SK_MaxS16) {
1410 x = SK_MaxS16;
1411 }
1412#endif
1413 return x;
1414}
1415
reed@google.com3b3e8952012-08-16 20:53:31 +00001416bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001417 SkIRect ibounds;
1418 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 return false;
1420 }
1421
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001422 SkMatrix inverse;
1423 // if we can't invert the CTM, we can't return local clip bounds
1424 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001425 if (bounds) {
1426 bounds->setEmpty();
1427 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001428 return false;
1429 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001431 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001432 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001433 // adjust it outwards in case we are antialiasing
1434 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001435
1436 // SkRect::iset() will correctly assert if we pass a value out of range
1437 // (when SkScalar==fixed), so we pin to legal values. This does not
1438 // really returnt the correct answer, but its the best we can do given
1439 // that we've promised to return SkRect (even though we support devices
1440 // that can be larger than 32K in width or height).
1441 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1442 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001443 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001444 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 inverse.mapRect(bounds, r);
1446 }
1447 return true;
1448}
1449
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001450bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001451 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001452 if (clip.isEmpty()) {
1453 if (bounds) {
1454 bounds->setEmpty();
1455 }
1456 return false;
1457 }
1458
1459 if (NULL != bounds) {
1460 *bounds = clip.getBounds();
1461 }
1462 return true;
1463}
1464
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465const SkMatrix& SkCanvas::getTotalMatrix() const {
1466 return *fMCRec->fMatrix;
1467}
1468
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001469SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001470 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1471 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001472 return kComplex_ClipType;
1473}
1474
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001476 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
bsalomon@google.come97f0852011-06-17 13:10:25 +00001479SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1480 int width, int height,
1481 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001482 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001483 if (device) {
1484 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1485 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001486 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001487 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001488 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001491SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001492 int width, int height,
1493 bool isOpaque) {
1494 SkDevice* device = this->getDevice();
1495 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001496 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001497 } else {
1498 return NULL;
1499 }
1500}
1501
1502
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503//////////////////////////////////////////////////////////////////////////////
1504// These are the virtual drawing methods
1505//////////////////////////////////////////////////////////////////////////////
1506
reed@google.com2a981812011-04-14 18:59:28 +00001507void SkCanvas::clear(SkColor color) {
1508 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001509 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001510 while (iter.next()) {
1511 iter.fDevice->clear(color);
1512 }
1513}
1514
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001516 this->internalDrawPaint(paint);
1517}
1518
1519void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001520 CHECK_SHADER_NOSETCONTEXT(paint);
1521
reed@google.com4e2b3d32011-04-07 14:18:59 +00001522 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001523
1524 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001525 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526 }
1527
reed@google.com4e2b3d32011-04-07 14:18:59 +00001528 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529}
1530
1531void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1532 const SkPaint& paint) {
1533 if ((long)count <= 0) {
1534 return;
1535 }
1536
reed@google.comea033602012-12-14 13:13:55 +00001537 CHECK_SHADER_NOSETCONTEXT(paint);
1538
reed@google.coma584aed2012-05-16 14:06:02 +00001539 if (paint.canComputeFastBounds()) {
1540 SkRect r;
1541 // special-case 2 points (common for drawing a single line)
1542 if (2 == count) {
1543 r.set(pts[0], pts[1]);
1544 } else {
1545 r.set(pts, count);
1546 }
1547 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001548 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001549 return;
1550 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001551 }
reed@google.coma584aed2012-05-16 14:06:02 +00001552
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553 SkASSERT(pts != NULL);
1554
reed@google.com4e2b3d32011-04-07 14:18:59 +00001555 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001556
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001558 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 }
reed@google.com4b226022011-01-11 18:32:13 +00001560
reed@google.com4e2b3d32011-04-07 14:18:59 +00001561 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562}
1563
1564void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001565 CHECK_SHADER_NOSETCONTEXT(paint);
1566
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567 if (paint.canComputeFastBounds()) {
1568 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001569 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 return;
1571 }
1572 }
reed@google.com4b226022011-01-11 18:32:13 +00001573
reed@google.com4e2b3d32011-04-07 14:18:59 +00001574 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575
1576 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001577 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578 }
1579
reed@google.com4e2b3d32011-04-07 14:18:59 +00001580 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581}
1582
reed@google.com4ed0fb72012-12-12 20:48:18 +00001583void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001584 CHECK_SHADER_NOSETCONTEXT(paint);
1585
reed@google.com4ed0fb72012-12-12 20:48:18 +00001586 if (paint.canComputeFastBounds()) {
1587 SkRect storage;
1588 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1589 return;
1590 }
1591 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001592
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001593 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1594
1595 while (iter.next()) {
1596 iter.fDevice->drawOval(iter, oval, looper.paint());
1597 }
1598
1599 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001600}
1601
1602void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001603 CHECK_SHADER_NOSETCONTEXT(paint);
1604
reed@google.com4ed0fb72012-12-12 20:48:18 +00001605 if (paint.canComputeFastBounds()) {
1606 SkRect storage;
1607 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1608 return;
1609 }
1610 }
1611
1612 if (rrect.isRect()) {
1613 // call the non-virtual version
1614 this->SkCanvas::drawRect(rrect.getBounds(), paint);
1615 } else {
1616 SkPath path;
1617 path.addRRect(rrect);
1618 // call the non-virtual version
1619 this->SkCanvas::drawPath(path, paint);
1620 }
1621}
1622
1623
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001625 CHECK_SHADER_NOSETCONTEXT(paint);
1626
reed@google.com93645112012-07-26 16:11:47 +00001627 if (!path.isFinite()) {
1628 return;
1629 }
1630
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001631 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001632 SkRect storage;
1633 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001634 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635 return;
1636 }
1637 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001638 if (path.isEmpty()) {
1639 if (path.isInverseFillType()) {
1640 this->internalDrawPaint(paint);
1641 }
1642 return;
1643 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644
reed@google.com4e2b3d32011-04-07 14:18:59 +00001645 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646
1647 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001648 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649 }
1650
reed@google.com4e2b3d32011-04-07 14:18:59 +00001651 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652}
1653
1654void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1655 const SkPaint* paint) {
1656 SkDEBUGCODE(bitmap.validate();)
1657
reed@google.com3d608122011-11-21 15:16:16 +00001658 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001659 SkRect bounds = {
1660 x, y,
1661 x + SkIntToScalar(bitmap.width()),
1662 y + SkIntToScalar(bitmap.height())
1663 };
1664 if (paint) {
1665 (void)paint->computeFastBounds(bounds, &bounds);
1666 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001667 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668 return;
1669 }
1670 }
reed@google.com4b226022011-01-11 18:32:13 +00001671
reed@android.com8a1c16f2008-12-17 15:59:43 +00001672 SkMatrix matrix;
1673 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001674 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675}
1676
reed@google.com9987ec32011-09-07 11:57:52 +00001677// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001678void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001679 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1681 return;
1682 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001683
reed@google.comea033602012-12-14 13:13:55 +00001684 CHECK_LOCKCOUNT_BALANCE(bitmap);
1685
reed@google.com3d608122011-11-21 15:16:16 +00001686 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001687 SkRect storage;
1688 const SkRect* bounds = &dst;
1689 if (paint) {
1690 bounds = &paint->computeFastBounds(dst, &storage);
1691 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001692 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001693 return;
1694 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 }
reed@google.com3d608122011-11-21 15:16:16 +00001696
reed@google.com33535f32012-09-25 15:37:50 +00001697 SkLazyPaint lazy;
1698 if (NULL == paint) {
1699 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001700 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001701
reed@google.com33535f32012-09-25 15:37:50 +00001702 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001703
reed@google.com33535f32012-09-25 15:37:50 +00001704 while (iter.next()) {
1705 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001706 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001707
reed@google.com33535f32012-09-25 15:37:50 +00001708 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709}
1710
reed@google.com71121732012-09-18 15:14:33 +00001711void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1712 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001713 SkDEBUGCODE(bitmap.validate();)
1714 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1715}
1716
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1718 const SkPaint* paint) {
1719 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001720 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721}
1722
reed@android.comf2b98d62010-12-20 18:26:13 +00001723void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1724 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001726 CHECK_LOCKCOUNT_BALANCE(bitmap);
reed@android.com9b039062009-02-11 15:09:58 +00001727
reed@google.com4e2b3d32011-04-07 14:18:59 +00001728 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001729
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001731 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 }
reed@android.com9b039062009-02-11 15:09:58 +00001733
reed@google.com4e2b3d32011-04-07 14:18:59 +00001734 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735}
1736
reed@google.com9987ec32011-09-07 11:57:52 +00001737void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1738 const SkIRect& center, const SkRect& dst,
1739 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001740 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001741 SkRect storage;
1742 const SkRect* bounds = &dst;
1743 if (paint) {
1744 bounds = &paint->computeFastBounds(dst, &storage);
1745 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001746 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001747 return;
1748 }
1749 }
1750
reed@google.com9987ec32011-09-07 11:57:52 +00001751 const int32_t w = bitmap.width();
1752 const int32_t h = bitmap.height();
1753
1754 SkIRect c = center;
1755 // pin center to the bounds of the bitmap
1756 c.fLeft = SkMax32(0, center.fLeft);
1757 c.fTop = SkMax32(0, center.fTop);
1758 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1759 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1760
reed@google.com71121732012-09-18 15:14:33 +00001761 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001762 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001763 };
1764 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001765 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001766 };
reed@google.com9987ec32011-09-07 11:57:52 +00001767 SkScalar dstX[4] = {
1768 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1769 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1770 };
1771 SkScalar dstY[4] = {
1772 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1773 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1774 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001775
reed@google.com9987ec32011-09-07 11:57:52 +00001776 if (dstX[1] > dstX[2]) {
1777 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1778 dstX[2] = dstX[1];
1779 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001780
reed@google.com9987ec32011-09-07 11:57:52 +00001781 if (dstY[1] > dstY[2]) {
1782 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1783 dstY[2] = dstY[1];
1784 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001785
reed@google.com9987ec32011-09-07 11:57:52 +00001786 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001787 SkRect s, d;
1788
reed@google.com9987ec32011-09-07 11:57:52 +00001789 s.fTop = srcY[y];
1790 s.fBottom = srcY[y+1];
1791 d.fTop = dstY[y];
1792 d.fBottom = dstY[y+1];
1793 for (int x = 0; x < 3; x++) {
1794 s.fLeft = srcX[x];
1795 s.fRight = srcX[x+1];
1796 d.fLeft = dstX[x];
1797 d.fRight = dstX[x+1];
1798 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1799 }
1800 }
1801}
1802
1803void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1804 const SkRect& dst, const SkPaint* paint) {
1805 SkDEBUGCODE(bitmap.validate();)
1806
1807 // Need a device entry-point, so gpu can use a mesh
1808 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1809}
1810
reed@google.comf67e4cf2011-03-15 20:56:58 +00001811class SkDeviceFilteredPaint {
1812public:
1813 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1814 SkDevice::TextFlags flags;
1815 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001816 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001817 newPaint->setFlags(flags.fFlags);
1818 newPaint->setHinting(flags.fHinting);
1819 fPaint = newPaint;
1820 } else {
1821 fPaint = &paint;
1822 }
1823 }
1824
reed@google.comf67e4cf2011-03-15 20:56:58 +00001825 const SkPaint& paint() const { return *fPaint; }
1826
1827private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001828 const SkPaint* fPaint;
1829 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001830};
1831
bungeman@google.com52c748b2011-08-22 21:30:43 +00001832void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1833 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001834 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001835 draw.fDevice->drawRect(draw, r, paint);
1836 } else {
1837 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001838 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001839 draw.fDevice->drawRect(draw, r, p);
1840 }
1841}
1842
1843void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1844 const char text[], size_t byteLength,
1845 SkScalar x, SkScalar y) {
1846 SkASSERT(byteLength == 0 || text != NULL);
1847
1848 // nothing to draw
1849 if (text == NULL || byteLength == 0 ||
1850 draw.fClip->isEmpty() ||
1851 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1852 return;
1853 }
1854
1855 SkScalar width = 0;
1856 SkPoint start;
1857
1858 start.set(0, 0); // to avoid warning
1859 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1860 SkPaint::kStrikeThruText_Flag)) {
1861 width = paint.measureText(text, byteLength);
1862
1863 SkScalar offsetX = 0;
1864 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1865 offsetX = SkScalarHalf(width);
1866 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1867 offsetX = width;
1868 }
1869 start.set(x - offsetX, y);
1870 }
1871
1872 if (0 == width) {
1873 return;
1874 }
1875
1876 uint32_t flags = paint.getFlags();
1877
1878 if (flags & (SkPaint::kUnderlineText_Flag |
1879 SkPaint::kStrikeThruText_Flag)) {
1880 SkScalar textSize = paint.getTextSize();
1881 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1882 SkRect r;
1883
1884 r.fLeft = start.fX;
1885 r.fRight = start.fX + width;
1886
1887 if (flags & SkPaint::kUnderlineText_Flag) {
1888 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1889 start.fY);
1890 r.fTop = offset;
1891 r.fBottom = offset + height;
1892 DrawRect(draw, paint, r, textSize);
1893 }
1894 if (flags & SkPaint::kStrikeThruText_Flag) {
1895 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1896 start.fY);
1897 r.fTop = offset;
1898 r.fBottom = offset + height;
1899 DrawRect(draw, paint, r, textSize);
1900 }
1901 }
1902}
1903
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904void SkCanvas::drawText(const void* text, size_t byteLength,
1905 SkScalar x, SkScalar y, 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()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001911 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001912 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001913 DrawTextDecorations(iter, dfp.paint(),
1914 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 }
1916
reed@google.com4e2b3d32011-04-07 14:18:59 +00001917 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001918}
1919
1920void SkCanvas::drawPosText(const void* text, size_t byteLength,
1921 const SkPoint pos[], const SkPaint& paint) {
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)
reed@google.com4b226022011-01-11 18:32:13 +00001925
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001927 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001929 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930 }
reed@google.com4b226022011-01-11 18:32:13 +00001931
reed@google.com4e2b3d32011-04-07 14:18:59 +00001932 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933}
1934
1935void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1936 const SkScalar xpos[], SkScalar constY,
1937 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001938 CHECK_SHADER_NOSETCONTEXT(paint);
1939
reed@google.com4e2b3d32011-04-07 14:18:59 +00001940 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001941
reed@android.com8a1c16f2008-12-17 15:59:43 +00001942 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001943 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001945 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946 }
reed@google.com4b226022011-01-11 18:32:13 +00001947
reed@google.com4e2b3d32011-04-07 14:18:59 +00001948 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949}
1950
1951void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1952 const SkPath& path, const SkMatrix* matrix,
1953 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001954 CHECK_SHADER_NOSETCONTEXT(paint);
1955
reed@google.com4e2b3d32011-04-07 14:18:59 +00001956 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957
1958 while (iter.next()) {
1959 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001960 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001961 }
1962
reed@google.com4e2b3d32011-04-07 14:18:59 +00001963 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964}
1965
djsollen@google.com56c69772011-11-08 19:00:26 +00001966#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001967void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1968 const SkPoint pos[], const SkPaint& paint,
1969 const SkPath& path, const SkMatrix* matrix) {
reed@google.comea033602012-12-14 13:13:55 +00001970 CHECK_SHADER_NOSETCONTEXT(paint);
1971
reed@google.com4e2b3d32011-04-07 14:18:59 +00001972 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001973
1974 while (iter.next()) {
1975 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001976 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001977 }
1978
reed@google.com4e2b3d32011-04-07 14:18:59 +00001979 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001980}
1981#endif
1982
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1984 const SkPoint verts[], const SkPoint texs[],
1985 const SkColor colors[], SkXfermode* xmode,
1986 const uint16_t indices[], int indexCount,
1987 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001988 CHECK_SHADER_NOSETCONTEXT(paint);
1989
reed@google.com4e2b3d32011-04-07 14:18:59 +00001990 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001991
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992 while (iter.next()) {
1993 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001994 colors, xmode, indices, indexCount,
1995 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 }
reed@google.com4b226022011-01-11 18:32:13 +00001997
reed@google.com4e2b3d32011-04-07 14:18:59 +00001998 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999}
2000
reed@android.comcb608442009-12-04 21:32:27 +00002001void SkCanvas::drawData(const void* data, size_t length) {
2002 // do nothing. Subclasses may do something with the data
2003}
2004
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005//////////////////////////////////////////////////////////////////////////////
2006// These methods are NOT virtual, and therefore must call back into virtual
2007// methods, rather than actually drawing themselves.
2008//////////////////////////////////////////////////////////////////////////////
2009
2010void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002011 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 SkPaint paint;
2013
2014 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002015 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002016 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002017 }
2018 this->drawPaint(paint);
2019}
2020
reed@android.com845fdac2009-06-23 03:01:32 +00002021void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022 SkPaint paint;
2023
2024 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002025 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002026 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002027 }
2028 this->drawPaint(paint);
2029}
2030
2031void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2032 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002033
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034 pt.set(x, y);
2035 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2036}
2037
2038void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2039 SkPoint pt;
2040 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002041
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042 pt.set(x, y);
2043 paint.setColor(color);
2044 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2045}
2046
2047void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2048 const SkPaint& paint) {
2049 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002050
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051 pts[0].set(x0, y0);
2052 pts[1].set(x1, y1);
2053 this->drawPoints(kLines_PointMode, 2, pts, paint);
2054}
2055
2056void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2057 SkScalar right, SkScalar bottom,
2058 const SkPaint& paint) {
2059 SkRect r;
2060
2061 r.set(left, top, right, bottom);
2062 this->drawRect(r, paint);
2063}
2064
2065void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2066 const SkPaint& paint) {
2067 if (radius < 0) {
2068 radius = 0;
2069 }
2070
2071 SkRect r;
2072 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002073 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002074}
2075
2076void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2077 const SkPaint& paint) {
2078 if (rx > 0 && ry > 0) {
2079 if (paint.canComputeFastBounds()) {
2080 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002081 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082 return;
2083 }
2084 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002085 SkRRect rrect;
2086 rrect.setRectXY(r, rx, ry);
2087 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088 } else {
2089 this->drawRect(r, paint);
2090 }
2091}
2092
reed@android.com8a1c16f2008-12-17 15:59:43 +00002093void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2094 SkScalar sweepAngle, bool useCenter,
2095 const SkPaint& paint) {
2096 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2097 this->drawOval(oval, paint);
2098 } else {
2099 SkPath path;
2100 if (useCenter) {
2101 path.moveTo(oval.centerX(), oval.centerY());
2102 }
2103 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2104 if (useCenter) {
2105 path.close();
2106 }
2107 this->drawPath(path, paint);
2108 }
2109}
2110
2111void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2112 const SkPath& path, SkScalar hOffset,
2113 SkScalar vOffset, const SkPaint& paint) {
2114 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002115
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 matrix.setTranslate(hOffset, vOffset);
2117 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2118}
2119
reed@android.comf76bacf2009-05-13 14:00:33 +00002120///////////////////////////////////////////////////////////////////////////////
2121
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002123 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124}
2125
reed@android.com8a1c16f2008-12-17 15:59:43 +00002126///////////////////////////////////////////////////////////////////////////////
2127///////////////////////////////////////////////////////////////////////////////
2128
2129SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002130 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131
2132 SkASSERT(canvas);
2133
2134 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2135 fDone = !fImpl->next();
2136}
2137
2138SkCanvas::LayerIter::~LayerIter() {
2139 fImpl->~SkDrawIter();
2140}
2141
2142void SkCanvas::LayerIter::next() {
2143 fDone = !fImpl->next();
2144}
2145
2146SkDevice* SkCanvas::LayerIter::device() const {
2147 return fImpl->getDevice();
2148}
2149
2150const SkMatrix& SkCanvas::LayerIter::matrix() const {
2151 return fImpl->getMatrix();
2152}
2153
2154const SkPaint& SkCanvas::LayerIter::paint() const {
2155 const SkPaint* paint = fImpl->getPaint();
2156 if (NULL == paint) {
2157 paint = &fDefaultPaint;
2158 }
2159 return *paint;
2160}
2161
2162const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2163int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2164int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002165
2166///////////////////////////////////////////////////////////////////////////////
2167
2168SkCanvas::ClipVisitor::~ClipVisitor() { }