blob: c491632759b8eaea6e3efb9dfc4f3795ed4cee8d [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);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001615 return;
1616 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001617 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001618 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1619 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001620 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001621
1622 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1623
1624 while (iter.next()) {
1625 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1626 }
1627
1628 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001629}
1630
1631
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001633 CHECK_SHADER_NOSETCONTEXT(paint);
1634
reed@google.com93645112012-07-26 16:11:47 +00001635 if (!path.isFinite()) {
1636 return;
1637 }
1638
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001639 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001640 SkRect storage;
1641 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001642 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643 return;
1644 }
1645 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001646 if (path.isEmpty()) {
1647 if (path.isInverseFillType()) {
1648 this->internalDrawPaint(paint);
1649 }
1650 return;
1651 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652
reed@google.com4e2b3d32011-04-07 14:18:59 +00001653 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654
1655 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001656 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657 }
1658
reed@google.com4e2b3d32011-04-07 14:18:59 +00001659 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660}
1661
1662void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1663 const SkPaint* paint) {
1664 SkDEBUGCODE(bitmap.validate();)
1665
reed@google.com3d608122011-11-21 15:16:16 +00001666 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001667 SkRect bounds = {
1668 x, y,
1669 x + SkIntToScalar(bitmap.width()),
1670 y + SkIntToScalar(bitmap.height())
1671 };
1672 if (paint) {
1673 (void)paint->computeFastBounds(bounds, &bounds);
1674 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001675 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676 return;
1677 }
1678 }
reed@google.com4b226022011-01-11 18:32:13 +00001679
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680 SkMatrix matrix;
1681 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001682 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683}
1684
reed@google.com9987ec32011-09-07 11:57:52 +00001685// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001686void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001687 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1689 return;
1690 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001691
reed@google.comea033602012-12-14 13:13:55 +00001692 CHECK_LOCKCOUNT_BALANCE(bitmap);
1693
reed@google.com3d608122011-11-21 15:16:16 +00001694 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001695 SkRect storage;
1696 const SkRect* bounds = &dst;
1697 if (paint) {
1698 bounds = &paint->computeFastBounds(dst, &storage);
1699 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001700 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001701 return;
1702 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703 }
reed@google.com3d608122011-11-21 15:16:16 +00001704
reed@google.com33535f32012-09-25 15:37:50 +00001705 SkLazyPaint lazy;
1706 if (NULL == paint) {
1707 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001709
reed@google.com33535f32012-09-25 15:37:50 +00001710 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001711
reed@google.com33535f32012-09-25 15:37:50 +00001712 while (iter.next()) {
1713 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001714 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001715
reed@google.com33535f32012-09-25 15:37:50 +00001716 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717}
1718
reed@google.com71121732012-09-18 15:14:33 +00001719void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1720 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001721 SkDEBUGCODE(bitmap.validate();)
1722 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1723}
1724
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1726 const SkPaint* paint) {
1727 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001728 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729}
1730
reed@android.comf2b98d62010-12-20 18:26:13 +00001731void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1732 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001734 CHECK_LOCKCOUNT_BALANCE(bitmap);
reed@android.com9b039062009-02-11 15:09:58 +00001735
reed@google.com4e2b3d32011-04-07 14:18:59 +00001736 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001737
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001739 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740 }
reed@android.com9b039062009-02-11 15:09:58 +00001741
reed@google.com4e2b3d32011-04-07 14:18:59 +00001742 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743}
1744
reed@google.com9987ec32011-09-07 11:57:52 +00001745void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1746 const SkIRect& center, const SkRect& dst,
1747 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001748 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001749 SkRect storage;
1750 const SkRect* bounds = &dst;
1751 if (paint) {
1752 bounds = &paint->computeFastBounds(dst, &storage);
1753 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001754 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001755 return;
1756 }
1757 }
1758
reed@google.com9987ec32011-09-07 11:57:52 +00001759 const int32_t w = bitmap.width();
1760 const int32_t h = bitmap.height();
1761
1762 SkIRect c = center;
1763 // pin center to the bounds of the bitmap
1764 c.fLeft = SkMax32(0, center.fLeft);
1765 c.fTop = SkMax32(0, center.fTop);
1766 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1767 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1768
reed@google.com71121732012-09-18 15:14:33 +00001769 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001770 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001771 };
1772 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001773 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001774 };
reed@google.com9987ec32011-09-07 11:57:52 +00001775 SkScalar dstX[4] = {
1776 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1777 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1778 };
1779 SkScalar dstY[4] = {
1780 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1781 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1782 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001783
reed@google.com9987ec32011-09-07 11:57:52 +00001784 if (dstX[1] > dstX[2]) {
1785 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1786 dstX[2] = dstX[1];
1787 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001788
reed@google.com9987ec32011-09-07 11:57:52 +00001789 if (dstY[1] > dstY[2]) {
1790 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1791 dstY[2] = dstY[1];
1792 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001793
reed@google.com9987ec32011-09-07 11:57:52 +00001794 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001795 SkRect s, d;
1796
reed@google.com9987ec32011-09-07 11:57:52 +00001797 s.fTop = srcY[y];
1798 s.fBottom = srcY[y+1];
1799 d.fTop = dstY[y];
1800 d.fBottom = dstY[y+1];
1801 for (int x = 0; x < 3; x++) {
1802 s.fLeft = srcX[x];
1803 s.fRight = srcX[x+1];
1804 d.fLeft = dstX[x];
1805 d.fRight = dstX[x+1];
1806 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1807 }
1808 }
1809}
1810
1811void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1812 const SkRect& dst, const SkPaint* paint) {
1813 SkDEBUGCODE(bitmap.validate();)
1814
1815 // Need a device entry-point, so gpu can use a mesh
1816 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1817}
1818
reed@google.comf67e4cf2011-03-15 20:56:58 +00001819class SkDeviceFilteredPaint {
1820public:
1821 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1822 SkDevice::TextFlags flags;
1823 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001824 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001825 newPaint->setFlags(flags.fFlags);
1826 newPaint->setHinting(flags.fHinting);
1827 fPaint = newPaint;
1828 } else {
1829 fPaint = &paint;
1830 }
1831 }
1832
reed@google.comf67e4cf2011-03-15 20:56:58 +00001833 const SkPaint& paint() const { return *fPaint; }
1834
1835private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001836 const SkPaint* fPaint;
1837 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001838};
1839
bungeman@google.com52c748b2011-08-22 21:30:43 +00001840void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1841 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001842 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001843 draw.fDevice->drawRect(draw, r, paint);
1844 } else {
1845 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001846 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001847 draw.fDevice->drawRect(draw, r, p);
1848 }
1849}
1850
1851void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1852 const char text[], size_t byteLength,
1853 SkScalar x, SkScalar y) {
1854 SkASSERT(byteLength == 0 || text != NULL);
1855
1856 // nothing to draw
1857 if (text == NULL || byteLength == 0 ||
1858 draw.fClip->isEmpty() ||
1859 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1860 return;
1861 }
1862
1863 SkScalar width = 0;
1864 SkPoint start;
1865
1866 start.set(0, 0); // to avoid warning
1867 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1868 SkPaint::kStrikeThruText_Flag)) {
1869 width = paint.measureText(text, byteLength);
1870
1871 SkScalar offsetX = 0;
1872 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1873 offsetX = SkScalarHalf(width);
1874 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1875 offsetX = width;
1876 }
1877 start.set(x - offsetX, y);
1878 }
1879
1880 if (0 == width) {
1881 return;
1882 }
1883
1884 uint32_t flags = paint.getFlags();
1885
1886 if (flags & (SkPaint::kUnderlineText_Flag |
1887 SkPaint::kStrikeThruText_Flag)) {
1888 SkScalar textSize = paint.getTextSize();
1889 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1890 SkRect r;
1891
1892 r.fLeft = start.fX;
1893 r.fRight = start.fX + width;
1894
1895 if (flags & SkPaint::kUnderlineText_Flag) {
1896 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1897 start.fY);
1898 r.fTop = offset;
1899 r.fBottom = offset + height;
1900 DrawRect(draw, paint, r, textSize);
1901 }
1902 if (flags & SkPaint::kStrikeThruText_Flag) {
1903 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1904 start.fY);
1905 r.fTop = offset;
1906 r.fBottom = offset + height;
1907 DrawRect(draw, paint, r, textSize);
1908 }
1909 }
1910}
1911
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912void SkCanvas::drawText(const void* text, size_t byteLength,
1913 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001914 CHECK_SHADER_NOSETCONTEXT(paint);
1915
reed@google.com4e2b3d32011-04-07 14:18:59 +00001916 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917
1918 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001919 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001920 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001921 DrawTextDecorations(iter, dfp.paint(),
1922 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001923 }
1924
reed@google.com4e2b3d32011-04-07 14:18:59 +00001925 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926}
1927
1928void SkCanvas::drawPosText(const void* text, size_t byteLength,
1929 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001930 CHECK_SHADER_NOSETCONTEXT(paint);
1931
reed@google.com4e2b3d32011-04-07 14:18:59 +00001932 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001933
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001935 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001937 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 }
reed@google.com4b226022011-01-11 18:32:13 +00001939
reed@google.com4e2b3d32011-04-07 14:18:59 +00001940 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941}
1942
1943void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1944 const SkScalar xpos[], SkScalar constY,
1945 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001946 CHECK_SHADER_NOSETCONTEXT(paint);
1947
reed@google.com4e2b3d32011-04-07 14:18:59 +00001948 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001949
reed@android.com8a1c16f2008-12-17 15:59:43 +00001950 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001951 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001953 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954 }
reed@google.com4b226022011-01-11 18:32:13 +00001955
reed@google.com4e2b3d32011-04-07 14:18:59 +00001956 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957}
1958
1959void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1960 const SkPath& path, const SkMatrix* matrix,
1961 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001962 CHECK_SHADER_NOSETCONTEXT(paint);
1963
reed@google.com4e2b3d32011-04-07 14:18:59 +00001964 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965
1966 while (iter.next()) {
1967 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001968 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969 }
1970
reed@google.com4e2b3d32011-04-07 14:18:59 +00001971 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972}
1973
djsollen@google.com56c69772011-11-08 19:00:26 +00001974#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001975void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1976 const SkPoint pos[], const SkPaint& paint,
1977 const SkPath& path, const SkMatrix* matrix) {
reed@google.comea033602012-12-14 13:13:55 +00001978 CHECK_SHADER_NOSETCONTEXT(paint);
1979
reed@google.com4e2b3d32011-04-07 14:18:59 +00001980 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001981
1982 while (iter.next()) {
1983 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001984 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001985 }
1986
reed@google.com4e2b3d32011-04-07 14:18:59 +00001987 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001988}
1989#endif
1990
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1992 const SkPoint verts[], const SkPoint texs[],
1993 const SkColor colors[], SkXfermode* xmode,
1994 const uint16_t indices[], int indexCount,
1995 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001996 CHECK_SHADER_NOSETCONTEXT(paint);
1997
reed@google.com4e2b3d32011-04-07 14:18:59 +00001998 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001999
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000 while (iter.next()) {
2001 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002002 colors, xmode, indices, indexCount,
2003 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004 }
reed@google.com4b226022011-01-11 18:32:13 +00002005
reed@google.com4e2b3d32011-04-07 14:18:59 +00002006 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007}
2008
reed@android.comcb608442009-12-04 21:32:27 +00002009void SkCanvas::drawData(const void* data, size_t length) {
2010 // do nothing. Subclasses may do something with the data
2011}
2012
reed@android.com8a1c16f2008-12-17 15:59:43 +00002013//////////////////////////////////////////////////////////////////////////////
2014// These methods are NOT virtual, and therefore must call back into virtual
2015// methods, rather than actually drawing themselves.
2016//////////////////////////////////////////////////////////////////////////////
2017
2018void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002019 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002020 SkPaint paint;
2021
2022 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002023 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002024 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002025 }
2026 this->drawPaint(paint);
2027}
2028
reed@android.com845fdac2009-06-23 03:01:32 +00002029void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030 SkPaint paint;
2031
2032 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002033 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002034 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 }
2036 this->drawPaint(paint);
2037}
2038
2039void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2040 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002041
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042 pt.set(x, y);
2043 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2044}
2045
2046void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2047 SkPoint pt;
2048 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002049
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050 pt.set(x, y);
2051 paint.setColor(color);
2052 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2053}
2054
2055void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2056 const SkPaint& paint) {
2057 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002058
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059 pts[0].set(x0, y0);
2060 pts[1].set(x1, y1);
2061 this->drawPoints(kLines_PointMode, 2, pts, paint);
2062}
2063
2064void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2065 SkScalar right, SkScalar bottom,
2066 const SkPaint& paint) {
2067 SkRect r;
2068
2069 r.set(left, top, right, bottom);
2070 this->drawRect(r, paint);
2071}
2072
2073void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2074 const SkPaint& paint) {
2075 if (radius < 0) {
2076 radius = 0;
2077 }
2078
2079 SkRect r;
2080 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002081 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082}
2083
2084void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2085 const SkPaint& paint) {
2086 if (rx > 0 && ry > 0) {
2087 if (paint.canComputeFastBounds()) {
2088 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002089 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 return;
2091 }
2092 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002093 SkRRect rrect;
2094 rrect.setRectXY(r, rx, ry);
2095 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096 } else {
2097 this->drawRect(r, paint);
2098 }
2099}
2100
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2102 SkScalar sweepAngle, bool useCenter,
2103 const SkPaint& paint) {
2104 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2105 this->drawOval(oval, paint);
2106 } else {
2107 SkPath path;
2108 if (useCenter) {
2109 path.moveTo(oval.centerX(), oval.centerY());
2110 }
2111 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2112 if (useCenter) {
2113 path.close();
2114 }
2115 this->drawPath(path, paint);
2116 }
2117}
2118
2119void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2120 const SkPath& path, SkScalar hOffset,
2121 SkScalar vOffset, const SkPaint& paint) {
2122 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002123
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 matrix.setTranslate(hOffset, vOffset);
2125 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2126}
2127
reed@android.comf76bacf2009-05-13 14:00:33 +00002128///////////////////////////////////////////////////////////////////////////////
2129
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132}
2133
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134///////////////////////////////////////////////////////////////////////////////
2135///////////////////////////////////////////////////////////////////////////////
2136
2137SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002138 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139
2140 SkASSERT(canvas);
2141
2142 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2143 fDone = !fImpl->next();
2144}
2145
2146SkCanvas::LayerIter::~LayerIter() {
2147 fImpl->~SkDrawIter();
2148}
2149
2150void SkCanvas::LayerIter::next() {
2151 fDone = !fImpl->next();
2152}
2153
2154SkDevice* SkCanvas::LayerIter::device() const {
2155 return fImpl->getDevice();
2156}
2157
2158const SkMatrix& SkCanvas::LayerIter::matrix() const {
2159 return fImpl->getMatrix();
2160}
2161
2162const SkPaint& SkCanvas::LayerIter::paint() const {
2163 const SkPaint* paint = fImpl->getPaint();
2164 if (NULL == paint) {
2165 paint = &fDefaultPaint;
2166 }
2167 return *paint;
2168}
2169
2170const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2171int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2172int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002173
2174///////////////////////////////////////////////////////////////////////////////
2175
2176SkCanvas::ClipVisitor::~ClipVisitor() { }