blob: 9dcbfdbc38891c65deb30feb8bf047dfc42022c4 [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"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkBounder.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@google.com97af1a62012-08-28 12:19:02 +000022#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000024#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000025#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000028#if SK_SUPPORT_GPU
29#include "GrRenderTarget.h"
30#endif
31
reed@google.comda17f752012-08-16 18:27:05 +000032// experimental for faster tiled drawing...
33//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000034
reed@android.com8a1c16f2008-12-17 15:59:43 +000035//#define SK_TRACE_SAVERESTORE
36
37#ifdef SK_TRACE_SAVERESTORE
38 static int gLayerCounter;
39 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
40 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
41
42 static int gRecCounter;
43 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
44 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
45
46 static int gCanvasCounter;
47 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
48 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
49#else
50 #define inc_layer()
51 #define dec_layer()
52 #define inc_rec()
53 #define dec_rec()
54 #define inc_canvas()
55 #define dec_canvas()
56#endif
57
reed@google.comea033602012-12-14 13:13:55 +000058#ifdef SK_DEBUG
59#include "SkPixelRef.h"
60
reed@google.comf53d0a92013-01-30 13:17:32 +000061/*
62 * Some pixelref subclasses can support being "locked" from another thread
63 * during the lock-scope of skia calling them. In these instances, this balance
64 * check will fail, but may not be indicative of a problem, so we allow a build
65 * flag to disable this check.
66 *
67 * Potentially another fix would be to have a (debug-only) virtual or flag on
68 * pixelref, which could tell us at runtime if this check is valid. That would
69 * eliminate the need for this heavy-handed build check.
70 */
71#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
72class AutoCheckLockCountBalance {
73public:
74 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
75};
76#else
reed@google.comea033602012-12-14 13:13:55 +000077class AutoCheckLockCountBalance {
78public:
79 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
80 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
81 }
82 ~AutoCheckLockCountBalance() {
83 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
84 SkASSERT(count == fLockCount);
85 }
86
87private:
88 const SkPixelRef* fPixelRef;
89 int fLockCount;
90};
reed@google.comf53d0a92013-01-30 13:17:32 +000091#endif
reed@google.comea033602012-12-14 13:13:55 +000092
93class AutoCheckNoSetContext {
94public:
95 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
96 this->assertNoSetContext(fPaint);
97 }
98 ~AutoCheckNoSetContext() {
99 this->assertNoSetContext(fPaint);
100 }
101
102private:
103 const SkPaint& fPaint;
104
105 void assertNoSetContext(const SkPaint& paint) {
106 SkShader* s = paint.getShader();
107 if (s) {
108 SkASSERT(!s->setContextHasBeenCalled());
109 }
110 }
111};
112
113#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
114#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
115
116#else
117 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
118 #define CHECK_SHADER_NOSETCONTEXT(paint)
119#endif
120
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000121typedef SkTLazy<SkPaint> SkLazyPaint;
122
reed@google.com97af1a62012-08-28 12:19:02 +0000123void SkCanvas::predrawNotify() {
124 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000125 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000126 }
127}
128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000131/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 The clip/matrix/proc are fields that reflect the top of the save/restore
133 stack. Whenever the canvas changes, it marks a dirty flag, and then before
134 these are used (assuming we're not on a layer) we rebuild these cache
135 values: they reflect the top of the save stack, but translated and clipped
136 by the device's XY offset and bitmap-bounds.
137*/
138struct DeviceCM {
139 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000140 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000141 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000143 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000145 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 : fNext(NULL) {
147 if (NULL != device) {
148 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000149 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 }
reed@google.com4b226022011-01-11 18:32:13 +0000151 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000153 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000155 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000157 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 fDevice->unref();
159 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000160 SkDELETE(fPaint);
161 }
reed@google.com4b226022011-01-11 18:32:13 +0000162
reed@google.com045e62d2011-10-24 12:19:46 +0000163 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
164 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000165 int x = fDevice->getOrigin().x();
166 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 int width = fDevice->width();
168 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 if ((x | y) == 0) {
171 fMatrix = &totalMatrix;
172 fClip = totalClip;
173 } else {
174 fMatrixStorage = totalMatrix;
175 fMatrixStorage.postTranslate(SkIntToScalar(-x),
176 SkIntToScalar(-y));
177 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 totalClip.translate(-x, -y, &fClip);
180 }
181
reed@google.com045e62d2011-10-24 12:19:46 +0000182 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
184 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000187 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 SkRegion::kDifference_Op);
189 }
reed@google.com4b226022011-01-11 18:32:13 +0000190
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000191 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193#ifdef SK_DEBUG
194 if (!fClip.isEmpty()) {
195 SkIRect deviceR;
196 deviceR.set(0, 0, width, height);
197 SkASSERT(deviceR.contains(fClip.getBounds()));
198 }
199#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000200 }
201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000203 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204};
205
206/* This is the record we keep for each save/restore level in the stack.
207 Since a level optionally copies the matrix and/or stack, we have pointers
208 for these fields. If the value is copied for this level, the copy is
209 stored in the ...Storage field, and the pointer points to that. If the
210 value is not copied for this level, we ignore ...Storage, and just point
211 at the corresponding value in the previous level in the stack.
212*/
213class SkCanvas::MCRec {
214public:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000215 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000216 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
217 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
218 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000219
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 DeviceCM* fLayer;
221 /* If there are any layers in the stack, this points to the top-most
222 one that is at or below this level in the stack (so we know what
223 bitmap/device to draw into from this level. This value is NOT
224 reference counted, since the real owner is either our fLayer field,
225 or a previous one in a lower level.)
226 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000227 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000229 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 if (NULL != prev) {
231 if (flags & SkCanvas::kMatrix_SaveFlag) {
232 fMatrixStorage = *prev->fMatrix;
233 fMatrix = &fMatrixStorage;
234 } else {
235 fMatrix = prev->fMatrix;
236 }
reed@google.com4b226022011-01-11 18:32:13 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000239 fRasterClipStorage = *prev->fRasterClip;
240 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 } else {
reed@google.com00177082011-10-12 14:34:30 +0000242 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 }
244
245 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000246 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248 fTopLayer = prev->fTopLayer;
249 } else { // no prev
250 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000253 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 fFilter = NULL;
255 fTopLayer = NULL;
256 }
257 fLayer = NULL;
258
259 // don't bother initializing fNext
260 inc_rec();
261 }
262 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000263 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 SkDELETE(fLayer);
265 dec_rec();
266 }
reed@google.com4b226022011-01-11 18:32:13 +0000267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268private:
reed@google.com00177082011-10-12 14:34:30 +0000269 SkMatrix fMatrixStorage;
270 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271};
272
273class SkDrawIter : public SkDraw {
274public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000275 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000276 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000277 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 canvas->updateDeviceCMCache();
279
reed@google.com90c07ea2012-04-13 13:50:27 +0000280 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fBounder = canvas->getBounder();
282 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000283 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 }
reed@google.com4b226022011-01-11 18:32:13 +0000285
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 bool next() {
287 // skip over recs with empty clips
288 if (fSkipEmptyClips) {
289 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
290 fCurrLayer = fCurrLayer->fNext;
291 }
292 }
293
reed@google.comf68c5e22012-02-24 16:38:58 +0000294 const DeviceCM* rec = fCurrLayer;
295 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296
297 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000298 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
299 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 fDevice = rec->fDevice;
301 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000303 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304
305 fCurrLayer = rec->fNext;
306 if (fBounder) {
307 fBounder->setClip(fClip);
308 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000310
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 return true;
312 }
313 return false;
314 }
reed@google.com4b226022011-01-11 18:32:13 +0000315
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000316 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000317 int getX() const { return fDevice->getOrigin().x(); }
318 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 const SkMatrix& getMatrix() const { return *fMatrix; }
320 const SkRegion& getClip() const { return *fClip; }
321 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000322
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323private:
324 SkCanvas* fCanvas;
325 const DeviceCM* fCurrLayer;
326 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 SkBool8 fSkipEmptyClips;
328
329 typedef SkDraw INHERITED;
330};
331
332/////////////////////////////////////////////////////////////////////////////
333
334class AutoDrawLooper {
335public:
reed@google.com8926b162012-03-23 15:36:36 +0000336 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000337 bool skipLayerForImageFilter = false,
338 const SkRect* bounds = NULL) : 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());
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000350 (void)canvas->internalSaveLayer(bounds, &tmp,
reed@google.com8926b162012-03-23 15:36:36 +0000351 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
352 // we'll clear the imageFilter for the actual draws in next(), so
353 // it will only be applied during the restore().
354 fDoClearImageFilter = true;
355 }
356
reed@google.com4e2b3d32011-04-07 14:18:59 +0000357 if (fLooper) {
358 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000359 fIsSimple = false;
360 } else {
361 // can we be marked as simple?
362 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000363 }
364 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000365
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000367 if (fDoClearImageFilter) {
368 fCanvas->internalRestore();
369 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000370 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000372
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 const SkPaint& paint() const {
374 SkASSERT(fPaint);
375 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000377
reed@google.com129ec222012-05-15 13:24:09 +0000378 bool next(SkDrawFilter::Type drawType) {
379 if (fDone) {
380 return false;
381 } else if (fIsSimple) {
382 fDone = true;
383 fPaint = &fOrigPaint;
384 return !fPaint->nothingToDraw();
385 } else {
386 return this->doNext(drawType);
387 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000388 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000389
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000391 SkLazyPaint fLazyPaint;
392 SkCanvas* fCanvas;
393 const SkPaint& fOrigPaint;
394 SkDrawLooper* fLooper;
395 SkDrawFilter* fFilter;
396 const SkPaint* fPaint;
397 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000398 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000399 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000400 bool fIsSimple;
401
402 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403};
404
reed@google.com129ec222012-05-15 13:24:09 +0000405bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000406 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000407 SkASSERT(!fIsSimple);
408 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
409
410 SkPaint* paint = fLazyPaint.set(fOrigPaint);
411
412 if (fDoClearImageFilter) {
413 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000414 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415
reed@google.com129ec222012-05-15 13:24:09 +0000416 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000417 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000418 return false;
419 }
420 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000421 if (!fFilter->filter(paint, drawType)) {
422 fDone = true;
423 return false;
424 }
reed@google.com129ec222012-05-15 13:24:09 +0000425 if (NULL == fLooper) {
426 // no looper means we only draw once
427 fDone = true;
428 }
429 }
430 fPaint = paint;
431
432 // if we only came in here for the imagefilter, mark us as done
433 if (!fLooper && !fFilter) {
434 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000435 }
436
437 // call this after any possible paint modifiers
438 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000439 fPaint = NULL;
440 return false;
441 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000442 return true;
443}
444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445/* Stack helper for managing a SkBounder. In the destructor, if we were
446 given a bounder, we call its commit() method, signifying that we are
447 done accumulating bounds for that draw.
448*/
449class SkAutoBounderCommit {
450public:
451 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
452 ~SkAutoBounderCommit() {
453 if (NULL != fBounder) {
454 fBounder->commit();
455 }
456 }
457private:
458 SkBounder* fBounder;
459};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000460#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461
462#include "SkColorPriv.h"
463
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464////////// macros to place around the internal draw calls //////////////////
465
reed@google.com8926b162012-03-23 15:36:36 +0000466#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000467 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000468 AutoDrawLooper looper(this, paint, true); \
469 while (looper.next(type)) { \
470 SkAutoBounderCommit ac(fBounder); \
471 SkDrawIter iter(this);
472
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000473#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000474 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000475 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000476 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 SkAutoBounderCommit ac(fBounder); \
478 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000479
reed@google.com4e2b3d32011-04-07 14:18:59 +0000480#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481
482////////////////////////////////////////////////////////////////////////////
483
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000484SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000486 fCachedLocalClipBounds.setEmpty();
487 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000488 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000489 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000490 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000491 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000492 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000493 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494
495 fMCRec = (MCRec*)fMCStack.push_back();
496 new (fMCRec) MCRec(NULL, 0);
497
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000498 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500
reed@google.com97af1a62012-08-28 12:19:02 +0000501 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000502
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000503 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504}
505
reed@google.comcde92112011-07-06 20:00:52 +0000506SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000507 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
508{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000509 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000510
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000511 this->init(NULL);
512}
513
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000514SkCanvas::SkCanvas(int width, int height)
515 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
516{
517 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000518
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000519 SkBitmap bitmap;
reed@google.com900ecf22014-02-20 20:55:37 +0000520 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000521 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
522}
523
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000524SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
526{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 inc_canvas();
528
529 this->init(device);
530}
531
532SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
534{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 inc_canvas();
536
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000537 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538}
539
540SkCanvas::~SkCanvas() {
541 // free up the contents of our deque
542 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000543 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000544
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 this->internalRestore(); // restore the last, since we're going away
546
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000547 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000548 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000549
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 dec_canvas();
551}
552
553SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
554 SkRefCnt_SafeAssign(fBounder, bounder);
555 return bounder;
556}
557
558SkDrawFilter* SkCanvas::getDrawFilter() const {
559 return fMCRec->fFilter;
560}
561
562SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
563 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
564 return filter;
565}
566
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000567SkMetaData& SkCanvas::getMetaData() {
568 // metadata users are rare, so we lazily allocate it. If that changes we
569 // can decide to just make it a field in the device (rather than a ptr)
570 if (NULL == fMetaData) {
571 fMetaData = new SkMetaData;
572 }
573 return *fMetaData;
574}
575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576///////////////////////////////////////////////////////////////////////////////
577
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000578void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000579 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000580 if (device) {
581 device->flush();
582 }
583}
584
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000585SkISize SkCanvas::getTopLayerSize() const {
586 SkBaseDevice* d = this->getTopDevice();
587 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
588}
589
590SkIPoint SkCanvas::getTopLayerOrigin() const {
591 SkBaseDevice* d = this->getTopDevice();
592 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
593}
594
595SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000596 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000597 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
598}
599
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000600SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 // 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
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000607SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000608 if (updateMatrixClip) {
609 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
610 }
reed@google.com9266fed2011-03-30 00:18:03 +0000611 return fMCRec->fTopLayer->fDevice;
612}
613
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000614SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 // 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);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000619 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620
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) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000669 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000670 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) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000677 SkBaseDevice* 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
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000699#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000700void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
701 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000702 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000703 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000704 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
705 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
706 device->accessBitmap(true);
707 device->writePixels(bitmap, x, y, config8888);
708 }
reed@google.com51df9e32010-12-23 19:29:18 +0000709 }
710}
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000711#endif
712
713bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
714 if (bitmap.getTexture()) {
715 return false;
716 }
717 SkBitmap bm(bitmap);
718 bm.lockPixels();
719 if (bm.getPixels()) {
720 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
721 }
722 return false;
723}
724
725bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
726 int x, int y) {
727 switch (origInfo.colorType()) {
728 case kUnknown_SkColorType:
729 case kIndex_8_SkColorType:
730 return false;
731 default:
732 break;
733 }
734 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
735 return false;
736 }
737
738 const SkISize size = this->getBaseLayerSize();
739 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
740 if (!target.intersect(0, 0, size.width(), size.height())) {
741 return false;
742 }
743
744 SkBaseDevice* device = this->getDevice();
745 if (!device) {
746 return false;
747 }
748
749 SkImageInfo info = origInfo;
750 // the intersect may have shrunk info's logical size
751 info.fWidth = target.width();
752 info.fHeight = target.height();
753
754 // if x or y are negative, then we have to adjust pixels
755 if (x > 0) {
756 x = 0;
757 }
758 if (y > 0) {
759 y = 0;
760 }
761 // here x,y are either 0 or negative
762 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
763
764 // The device can assert that the requested area is always contained in its bounds
765 return device->writePixelsDirect(info, pixels, rowBytes, target.x(), target.y());
766}
reed@google.com51df9e32010-12-23 19:29:18 +0000767
junov@google.com4370aed2012-01-18 16:21:08 +0000768SkCanvas* SkCanvas::canvasForDrawIter() {
769 return this;
770}
771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772//////////////////////////////////////////////////////////////////////////////
773
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774void SkCanvas::updateDeviceCMCache() {
775 if (fDeviceCMDirty) {
776 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000777 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000781 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000783 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000785 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 } while ((layer = layer->fNext) != NULL);
787 }
788 fDeviceCMDirty = false;
789 }
790}
791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792///////////////////////////////////////////////////////////////////////////////
793
794int SkCanvas::internalSave(SaveFlags flags) {
795 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000796
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 MCRec* newTop = (MCRec*)fMCStack.push_back();
798 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000799
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000801
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000802 if (SkCanvas::kClip_SaveFlag & flags) {
803 fClipStack.save();
804 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000805
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 return saveCount;
807}
808
809int SkCanvas::save(SaveFlags flags) {
810 // call shared impl
811 return this->internalSave(flags);
812}
813
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
815 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
816}
817
junov@chromium.orga907ac32012-02-24 21:54:07 +0000818bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000819 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000820 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000821 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000822 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000823 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000824 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000825
826 if (imageFilter) {
827 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
828 // Filters may grow the bounds beyond the device bounds.
829 op = SkRegion::kReplace_Op;
830 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000831 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 if (NULL != bounds) {
833 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000834
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 this->getTotalMatrix().mapRect(&r, *bounds);
836 r.roundOut(&ir);
837 // early exit if the layer's bounds are clipped out
838 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000839 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000840 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000841 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000842 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 }
844 } else { // no user bounds, so just use the clip
845 ir = clipBounds;
846 }
847
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000848 if (bounds_affects_clip(flags)) {
849 fClipStack.clipDevRect(ir, op);
850 // early exit if the clip is now empty
851 if (!fMCRec->fRasterClip->op(ir, op)) {
852 return false;
853 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000854 }
855
856 if (intersection) {
857 *intersection = ir;
858 }
859 return true;
860}
861
862int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
863 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000864 return this->internalSaveLayer(bounds, paint, flags, false);
865}
866
reed@google.com76f10a32014-02-05 15:32:21 +0000867static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000868 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000869 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000870 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000871}
872
reed@google.com8926b162012-03-23 15:36:36 +0000873int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
874 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000875 // do this before we create the layer. We don't call the public save() since
876 // that would invoke a possibly overridden virtual
877 int count = this->internalSave(flags);
878
879 fDeviceCMDirty = true;
880
881 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000882 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 return count;
884 }
885
reed@google.comb55deeb2012-01-06 14:43:09 +0000886 // Kill the imagefilter if our device doesn't allow it
887 SkLazyPaint lazyP;
888 if (paint && paint->getImageFilter()) {
889 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000890 if (justForImageFilter) {
891 // early exit if the layer was just for the imageFilter
892 return count;
893 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000894 SkPaint* p = lazyP.set(*paint);
895 p->setImageFilter(NULL);
896 paint = p;
897 }
898 }
899
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000900 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
901 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
902 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000904 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000905 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000906 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000907 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000908 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000909 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000910 if (NULL == device) {
911 SkDebugf("Unable to create device for layer.");
912 return count;
913 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000914
reed@google.com6f8f2922011-03-04 22:27:10 +0000915 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000916 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 device->unref();
918
919 layer->fNext = fMCRec->fTopLayer;
920 fMCRec->fLayer = layer;
921 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
922
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000923 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 return count;
925}
926
927int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
928 SaveFlags flags) {
929 if (0xFF == alpha) {
930 return this->saveLayer(bounds, NULL, flags);
931 } else {
932 SkPaint tmpPaint;
933 tmpPaint.setAlpha(alpha);
934 return this->saveLayer(bounds, &tmpPaint, flags);
935 }
936}
937
938void SkCanvas::restore() {
939 // check for underflow
940 if (fMCStack.count() > 1) {
941 this->internalRestore();
942 }
943}
944
945void SkCanvas::internalRestore() {
946 SkASSERT(fMCStack.count() != 0);
947
948 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000949 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000951 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
952 fClipStack.restore();
953 }
954
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000955 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 DeviceCM* layer = fMCRec->fLayer; // may be null
957 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
958 fMCRec->fLayer = NULL;
959
960 // now do the normal restore()
961 fMCRec->~MCRec(); // balanced in save()
962 fMCStack.pop_back();
963 fMCRec = (MCRec*)fMCStack.back();
964
965 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
966 since if we're being recorded, we don't want to record this (the
967 recorder will have already recorded the restore).
968 */
969 if (NULL != layer) {
970 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000971 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000972 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
973 layer->fPaint);
974 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000976
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000977 SkASSERT(fSaveLayerCount > 0);
978 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 }
980 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000981 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982}
983
984int SkCanvas::getSaveCount() const {
985 return fMCStack.count();
986}
987
988void SkCanvas::restoreToCount(int count) {
989 // sanity check
990 if (count < 1) {
991 count = 1;
992 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000993
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000994 int n = this->getSaveCount() - count;
995 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 this->restore();
997 }
998}
999
reed@google.com7c202932011-12-14 18:48:05 +00001000bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001001 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001002}
1003
reed@google.com76f10a32014-02-05 15:32:21 +00001004SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1005 return this->onNewSurface(info);
1006}
1007
1008SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1009 SkBaseDevice* dev = this->getDevice();
1010 return dev ? dev->newSurface(info) : NULL;
1011}
1012
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001013SkImageInfo SkCanvas::imageInfo() const {
1014 SkBaseDevice* dev = this->getDevice();
1015 if (dev) {
1016 return dev->imageInfo();
1017 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001018 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001019 }
1020}
1021
1022const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1023 return this->onPeekPixels(info, rowBytes);
1024}
1025
1026const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1027 SkBaseDevice* dev = this->getDevice();
1028 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1029}
1030
1031SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1032 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1033 if (NULL == fAddr) {
1034 fInfo = canvas->imageInfo();
1035 if (kUnknown_SkColorType == fInfo.colorType() ||
1036 !fBitmap.allocPixels(fInfo))
1037 {
1038 return; // failure, fAddr is NULL
1039 }
1040 fBitmap.lockPixels();
1041 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1042 return; // failure, fAddr is NULL
1043 }
1044 fAddr = fBitmap.getPixels();
1045 fRowBytes = fBitmap.rowBytes();
1046 }
1047 SkASSERT(fAddr); // success
1048}
1049
1050bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1051 if (fAddr) {
1052 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1053 NULL, NULL);
1054 } else {
1055 bitmap->reset();
1056 return false;
1057 }
1058}
1059
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001060void SkCanvas::onPushCull(const SkRect& cullRect) {
1061 // do nothing. Subclasses may do something
1062}
1063
1064void SkCanvas::onPopCull() {
1065 // do nothing. Subclasses may do something
1066}
1067
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068/////////////////////////////////////////////////////////////////////////////
1069
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001070void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001072 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 return;
1074 }
1075
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001076 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001078 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001080
1081 SkDEBUGCODE(bitmap.validate();)
1082 CHECK_LOCKCOUNT_BALANCE(bitmap);
1083
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001084 SkRect storage;
1085 const SkRect* bounds = NULL;
1086 if (paint && paint->canComputeFastBounds()) {
1087 bitmap.getBounds(&storage);
1088 matrix.mapRect(&storage);
1089 bounds = &paint->computeFastBounds(storage, &storage);
1090 }
1091
1092 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001093
1094 while (iter.next()) {
1095 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1096 }
1097
1098 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099}
1100
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001101void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001102 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 SkPaint tmp;
1104 if (NULL == paint) {
1105 tmp.setDither(true);
1106 paint = &tmp;
1107 }
reed@google.com4b226022011-01-11 18:32:13 +00001108
reed@google.com8926b162012-03-23 15:36:36 +00001109 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001111 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001112 paint = &looper.paint();
1113 SkImageFilter* filter = paint->getImageFilter();
1114 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001115 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001116 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001117 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001118 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001119 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001120 SkMatrix matrix = *iter.fMatrix;
1121 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001122 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001123 SkPaint tmpUnfiltered(*paint);
1124 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001125 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1126 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001127 }
1128 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001129 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001130 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001132 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133}
1134
reed@google.com8926b162012-03-23 15:36:36 +00001135void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1136 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001137 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001138 return;
1139 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001140 SkDEBUGCODE(bitmap.validate();)
1141 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001142
reed@google.com8926b162012-03-23 15:36:36 +00001143 SkPaint tmp;
1144 if (NULL == paint) {
1145 paint = &tmp;
1146 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001147
reed@google.com8926b162012-03-23 15:36:36 +00001148 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001149
reed@google.com8926b162012-03-23 15:36:36 +00001150 while (iter.next()) {
1151 paint = &looper.paint();
1152 SkImageFilter* filter = paint->getImageFilter();
1153 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1154 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001155 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001156 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001157 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001158 SkMatrix matrix = *iter.fMatrix;
1159 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001160 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001161 SkPaint tmpUnfiltered(*paint);
1162 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001163 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001164 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001165 }
1166 } else {
1167 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1168 }
1169 }
1170 LOOPER_END
1171}
1172
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173/////////////////////////////////////////////////////////////////////////////
1174
1175bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1176 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001177 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 return fMCRec->fMatrix->preTranslate(dx, dy);
1179}
1180
1181bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1182 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001183 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 return fMCRec->fMatrix->preScale(sx, sy);
1185}
1186
1187bool SkCanvas::rotate(SkScalar degrees) {
1188 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001189 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 return fMCRec->fMatrix->preRotate(degrees);
1191}
1192
1193bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1194 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001195 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 return fMCRec->fMatrix->preSkew(sx, sy);
1197}
1198
1199bool SkCanvas::concat(const SkMatrix& matrix) {
1200 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001201 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 return fMCRec->fMatrix->preConcat(matrix);
1203}
1204
1205void SkCanvas::setMatrix(const SkMatrix& matrix) {
1206 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001207 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 *fMCRec->fMatrix = matrix;
1209}
1210
1211// this is not virtual, so it must call a virtual method so that subclasses
1212// will see its action
1213void SkCanvas::resetMatrix() {
1214 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001215
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216 matrix.reset();
1217 this->setMatrix(matrix);
1218}
1219
1220//////////////////////////////////////////////////////////////////////////////
1221
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001222void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001223 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1224 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001225}
1226
1227void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001228#ifdef SK_ENABLE_CLIP_QUICKREJECT
1229 if (SkRegion::kIntersect_Op == op) {
1230 if (fMCRec->fRasterClip->isEmpty()) {
1231 return false;
1232 }
1233
reed@google.com3b3e8952012-08-16 20:53:31 +00001234 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001235 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001236 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001237
1238 fClipStack.clipEmpty();
1239 return fMCRec->fRasterClip->setEmpty();
1240 }
1241 }
1242#endif
1243
reed@google.com5c3d1472011-02-22 19:12:23 +00001244 AutoValidateClip avc(this);
1245
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001247 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001248 if (!fAllowSoftClip) {
1249 edgeStyle = kHard_ClipEdgeStyle;
1250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251
1252 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001253 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001254 // the matrix. This means we don't have to a) make a path, and b) tell
1255 // the region code to scan-convert the path, only to discover that it
1256 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258
1259 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001260 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1261 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001263 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001264 // and clip against that, since it can handle any matrix. However, to
1265 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1266 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 SkPath path;
1268
1269 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001270 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 }
1272}
1273
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001274static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1275 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001276 // base is used to limit the size (and therefore memory allocation) of the
1277 // region that results from scan converting devPath.
1278 SkRegion base;
1279
reed@google.com819c9212011-02-23 18:56:55 +00001280 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001281 // since we are intersect, we can do better (tighter) with currRgn's
1282 // bounds, than just using the device. However, if currRgn is complex,
1283 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001284 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001285 // FIXME: we should also be able to do this when currClip->isBW(),
1286 // but relaxing the test above triggers GM asserts in
1287 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001288 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001289 } else {
reed@google.com00177082011-10-12 14:34:30 +00001290 base.setRect(currClip->getBounds());
1291 SkRasterClip clip;
1292 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001293 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001294 }
reed@google.com819c9212011-02-23 18:56:55 +00001295 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001296 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001297 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001298 currClip->setEmpty();
1299 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001300 }
1301
junov@chromium.orga907ac32012-02-24 21:54:07 +00001302 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001303
1304 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001305 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001306 } else {
reed@google.com00177082011-10-12 14:34:30 +00001307 SkRasterClip clip;
1308 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001309 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001310 }
1311 }
1312}
1313
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001314void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001315 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001316 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001317 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1318 } else {
1319 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001320 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001321}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001322
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001323void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001324 SkRRect transformedRRect;
1325 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1326 AutoValidateClip avc(this);
1327
1328 fDeviceCMDirty = true;
1329 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001330 if (!fAllowSoftClip) {
1331 edgeStyle = kHard_ClipEdgeStyle;
1332 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001333
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001334 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001335
1336 SkPath devPath;
1337 devPath.addRRect(transformedRRect);
1338
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001339 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1340 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001341 }
1342
1343 SkPath path;
1344 path.addRRect(rrect);
1345 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001346 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001347}
1348
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001349void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001350 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1351 SkRect r;
1352 if (!path.isInverseFillType() && path.isRect(&r)) {
1353 this->onClipRect(r, op, edgeStyle);
1354 } else {
1355 this->onClipPath(path, op, edgeStyle);
1356 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001357}
1358
1359void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001360#ifdef SK_ENABLE_CLIP_QUICKREJECT
1361 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1362 if (fMCRec->fRasterClip->isEmpty()) {
1363 return false;
1364 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001365
reed@google.com3b3e8952012-08-16 20:53:31 +00001366 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001367 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001368 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001369
reed@google.comda17f752012-08-16 18:27:05 +00001370 fClipStack.clipEmpty();
1371 return fMCRec->fRasterClip->setEmpty();
1372 }
1373 }
1374#endif
1375
reed@google.com5c3d1472011-02-22 19:12:23 +00001376 AutoValidateClip avc(this);
1377
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001379 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 if (!fAllowSoftClip) {
1381 edgeStyle = kHard_ClipEdgeStyle;
1382 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383
1384 SkPath devPath;
1385 path.transform(*fMCRec->fMatrix, &devPath);
1386
reed@google.comfe701122011-11-08 19:41:23 +00001387 // Check if the transfomation, or the original path itself
1388 // made us empty. Note this can also happen if we contained NaN
1389 // values. computing the bounds detects this, and will set our
1390 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1391 if (devPath.getBounds().isEmpty()) {
1392 // resetting the path will remove any NaN or other wanky values
1393 // that might upset our scan converter.
1394 devPath.reset();
1395 }
1396
reed@google.com5c3d1472011-02-22 19:12:23 +00001397 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001399
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001400 if (fAllowSimplifyClip) {
1401 devPath.reset();
1402 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1403 const SkClipStack* clipStack = getClipStack();
1404 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1405 const SkClipStack::Element* element;
1406 while ((element = iter.next())) {
1407 SkClipStack::Element::Type type = element->getType();
1408 if (type == SkClipStack::Element::kEmpty_Type) {
1409 continue;
1410 }
1411 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001412 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001413 SkRegion::Op elementOp = element->getOp();
1414 if (elementOp == SkRegion::kReplace_Op) {
1415 devPath = operand;
1416 } else {
1417 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1418 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001419 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1420 // perhaps we need an API change to avoid this sort of mixed-signals about
1421 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001422 if (element->isAA()) {
1423 edgeStyle = kSoft_ClipEdgeStyle;
1424 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001425 }
1426 op = SkRegion::kReplace_Op;
1427 }
1428
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001429 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430}
1431
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001432void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001433 bool inverseFilled) {
1434 // This is for updating the clip conservatively using only bounds
1435 // information.
1436 // Contract:
1437 // The current clip must contain the true clip. The true
1438 // clip is the clip that would have normally been computed
1439 // by calls to clipPath and clipRRect
1440 // Objective:
1441 // Keep the current clip as small as possible without
1442 // breaking the contract, using only clip bounding rectangles
1443 // (for performance).
1444
1445 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1446 // don't have to worry about getting caught in a loop. Thus anywhere
1447 // we call a virtual method, we explicitly prefix it with
1448 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001449
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001450 if (inverseFilled) {
1451 switch (op) {
1452 case SkRegion::kIntersect_Op:
1453 case SkRegion::kDifference_Op:
1454 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001455 // the clip unchanged conservatively respects the contract.
1456 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001457 case SkRegion::kUnion_Op:
1458 case SkRegion::kReplace_Op:
1459 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001460 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001461 // These ops can grow the current clip up to the extents of
1462 // the input clip, which is inverse filled, so we just set
1463 // the current clip to the device bounds.
1464 SkRect deviceBounds;
1465 SkIRect deviceIBounds;
1466 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001467 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001468 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1469 // set the clip in device space
1470 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001471 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001472 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001473 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001474 break;
1475 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001476 default:
1477 SkASSERT(0); // unhandled op?
1478 }
1479 } else {
1480 // Not inverse filled
1481 switch (op) {
1482 case SkRegion::kIntersect_Op:
1483 case SkRegion::kUnion_Op:
1484 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001485 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1486 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001487 case SkRegion::kDifference_Op:
1488 // Difference can only shrink the current clip.
1489 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001490 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001491 case SkRegion::kReverseDifference_Op:
1492 // To reverse, we swap in the bounds with a replace op.
1493 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001494 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1495 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001496 case SkRegion::kXOR_Op:
1497 // Be conservative, based on (A XOR B) always included in (A union B),
1498 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001499 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1500 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001501 default:
1502 SkASSERT(0); // unhandled op?
1503 }
1504 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001505}
1506
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001507void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509}
1510
1511void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001512 AutoValidateClip avc(this);
1513
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001515 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516
reed@google.com5c3d1472011-02-22 19:12:23 +00001517 // todo: signal fClipStack that we have a region, and therefore (I guess)
1518 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001519 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001520
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001521 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522}
1523
reed@google.com819c9212011-02-23 18:56:55 +00001524#ifdef SK_DEBUG
1525void SkCanvas::validateClip() const {
1526 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001527 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001528 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001529 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001530 return;
1531 }
1532
reed@google.com819c9212011-02-23 18:56:55 +00001533 SkIRect ir;
1534 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001535 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001536
robertphillips@google.com80214e22012-07-20 15:33:18 +00001537 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001538 const SkClipStack::Element* element;
1539 while ((element = iter.next()) != NULL) {
1540 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001541 case SkClipStack::Element::kRect_Type:
1542 element->getRect().round(&ir);
1543 tmpClip.op(ir, element->getOp());
1544 break;
1545 case SkClipStack::Element::kEmpty_Type:
1546 tmpClip.setEmpty();
1547 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001548 default: {
1549 SkPath path;
1550 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001552 break;
1553 }
reed@google.com819c9212011-02-23 18:56:55 +00001554 }
1555 }
reed@google.com819c9212011-02-23 18:56:55 +00001556}
1557#endif
1558
reed@google.com90c07ea2012-04-13 13:50:27 +00001559void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001560 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001561 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001562
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001563 static const SkRect kEmpty = { 0, 0, 0, 0 };
1564 while ((element = iter.next()) != NULL) {
1565 switch (element->getType()) {
1566 case SkClipStack::Element::kPath_Type:
1567 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1568 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001569 case SkClipStack::Element::kRRect_Type:
1570 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1571 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001572 case SkClipStack::Element::kRect_Type:
1573 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1574 break;
1575 case SkClipStack::Element::kEmpty_Type:
1576 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1577 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001578 }
1579 }
1580}
1581
reed@google.com5c3d1472011-02-22 19:12:23 +00001582///////////////////////////////////////////////////////////////////////////////
1583
reed@google.com754de5f2014-02-24 19:38:20 +00001584bool SkCanvas::isClipEmpty() const {
1585 return fMCRec->fRasterClip->isEmpty();
1586}
1587
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001588bool SkCanvas::isClipRect() const {
1589 return fMCRec->fRasterClip->isRect();
1590}
1591
reed@google.com3b3e8952012-08-16 20:53:31 +00001592bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001593
reed@google.com16078632011-12-06 18:56:37 +00001594 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001595 return true;
1596
reed@google.com00177082011-10-12 14:34:30 +00001597 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598 return true;
1599 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600
tomhudson@google.com8d430182011-06-06 19:11:19 +00001601 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001602 SkRect dst;
1603 fMCRec->fMatrix->mapRect(&dst, rect);
1604 SkIRect idst;
1605 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001606 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001607 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001608 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001609
reed@android.coma380ae42009-07-21 01:17:02 +00001610 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001611 // TODO: should we use | instead, or compare all 4 at once?
1612 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001613 return true;
1614 }
reed@google.comc0784db2013-12-13 21:16:12 +00001615 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001616 return true;
1617 }
1618 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620}
1621
reed@google.com3b3e8952012-08-16 20:53:31 +00001622bool SkCanvas::quickReject(const SkPath& path) const {
1623 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624}
1625
reed@google.com3b3e8952012-08-16 20:53:31 +00001626bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001627 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001628 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 return false;
1630 }
1631
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001632 SkMatrix inverse;
1633 // if we can't invert the CTM, we can't return local clip bounds
1634 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001635 if (bounds) {
1636 bounds->setEmpty();
1637 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001638 return false;
1639 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001641 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001642 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001643 // adjust it outwards in case we are antialiasing
1644 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001645
reed@google.com8f4d2302013-12-17 16:44:46 +00001646 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1647 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648 inverse.mapRect(bounds, r);
1649 }
1650 return true;
1651}
1652
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001653bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001654 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001655 if (clip.isEmpty()) {
1656 if (bounds) {
1657 bounds->setEmpty();
1658 }
1659 return false;
1660 }
1661
1662 if (NULL != bounds) {
1663 *bounds = clip.getBounds();
1664 }
1665 return true;
1666}
1667
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668const SkMatrix& SkCanvas::getTotalMatrix() const {
1669 return *fMCRec->fMatrix;
1670}
1671
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001672#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001673SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001674 if (fMCRec->fRasterClip->isEmpty()) {
1675 return kEmpty_ClipType;
1676 }
1677 if (fMCRec->fRasterClip->isRect()) {
1678 return kRect_ClipType;
1679 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001680 return kComplex_ClipType;
1681}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001682#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001683
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001684#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001685const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001686 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001688#endif
1689
1690const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1691 return fMCRec->fRasterClip->forceGetBW();
1692}
1693
1694void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1695 path->reset();
1696
1697 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1698 if (rgn.isEmpty()) {
1699 return;
1700 }
1701 (void)rgn.getBoundaryPath(path);
1702}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001704SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001705 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001706 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707}
1708
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001709GrContext* SkCanvas::getGrContext() {
1710#if SK_SUPPORT_GPU
1711 SkBaseDevice* device = this->getTopDevice();
1712 if (NULL != device) {
1713 GrRenderTarget* renderTarget = device->accessRenderTarget();
1714 if (NULL != renderTarget) {
1715 return renderTarget->getContext();
1716 }
1717 }
1718#endif
1719
1720 return NULL;
1721
1722}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001723
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001724void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1725 const SkPaint& paint) {
1726 if (outer.isEmpty()) {
1727 return;
1728 }
1729 if (inner.isEmpty()) {
1730 this->drawRRect(outer, paint);
1731 return;
1732 }
1733
1734 // We don't have this method (yet), but technically this is what we should
1735 // be able to assert...
1736 // SkASSERT(outer.contains(inner));
1737 //
1738 // For now at least check for containment of bounds
1739 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1740
1741 this->onDrawDRRect(outer, inner, paint);
1742}
1743
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744//////////////////////////////////////////////////////////////////////////////
1745// These are the virtual drawing methods
1746//////////////////////////////////////////////////////////////////////////////
1747
reed@google.com2a981812011-04-14 18:59:28 +00001748void SkCanvas::clear(SkColor color) {
1749 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001750 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001751 while (iter.next()) {
1752 iter.fDevice->clear(color);
1753 }
1754}
1755
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001757 this->internalDrawPaint(paint);
1758}
1759
1760void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001761 CHECK_SHADER_NOSETCONTEXT(paint);
1762
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001763 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764
1765 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001766 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767 }
1768
reed@google.com4e2b3d32011-04-07 14:18:59 +00001769 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770}
1771
1772void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1773 const SkPaint& paint) {
1774 if ((long)count <= 0) {
1775 return;
1776 }
1777
reed@google.comea033602012-12-14 13:13:55 +00001778 CHECK_SHADER_NOSETCONTEXT(paint);
1779
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001780 SkRect r, storage;
1781 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001782 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001783 // special-case 2 points (common for drawing a single line)
1784 if (2 == count) {
1785 r.set(pts[0], pts[1]);
1786 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001787 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001788 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001789 bounds = &paint.computeFastStrokeBounds(r, &storage);
1790 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001791 return;
1792 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001793 }
reed@google.coma584aed2012-05-16 14:06:02 +00001794
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 SkASSERT(pts != NULL);
1796
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001797 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001798
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001800 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801 }
reed@google.com4b226022011-01-11 18:32:13 +00001802
reed@google.com4e2b3d32011-04-07 14:18:59 +00001803 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001804}
1805
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001806void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001807 CHECK_SHADER_NOSETCONTEXT(paint);
1808
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001809 SkRect storage;
1810 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001812 bounds = &paint.computeFastBounds(r, &storage);
1813 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 return;
1815 }
1816 }
reed@google.com4b226022011-01-11 18:32:13 +00001817
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001818 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819
1820 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001821 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 }
1823
reed@google.com4e2b3d32011-04-07 14:18:59 +00001824 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825}
1826
reed@google.com4ed0fb72012-12-12 20:48:18 +00001827void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001828 CHECK_SHADER_NOSETCONTEXT(paint);
1829
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001830 SkRect storage;
1831 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001832 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001833 bounds = &paint.computeFastBounds(oval, &storage);
1834 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001835 return;
1836 }
1837 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001838
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001839 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001840
1841 while (iter.next()) {
1842 iter.fDevice->drawOval(iter, oval, looper.paint());
1843 }
1844
1845 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001846}
1847
1848void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001849 CHECK_SHADER_NOSETCONTEXT(paint);
1850
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001851 SkRect storage;
1852 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001853 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001854 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1855 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001856 return;
1857 }
1858 }
1859
1860 if (rrect.isRect()) {
1861 // call the non-virtual version
1862 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001863 return;
1864 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001865 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001866 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1867 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001868 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001869
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001870 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001871
1872 while (iter.next()) {
1873 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1874 }
1875
1876 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001877}
1878
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001879void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1880 const SkPaint& paint) {
1881 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001882
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001883 SkRect storage;
1884 const SkRect* bounds = NULL;
1885 if (paint.canComputeFastBounds()) {
1886 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1887 if (this->quickReject(*bounds)) {
1888 return;
1889 }
1890 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001891
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001892 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001893
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001894 while (iter.next()) {
1895 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1896 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001897
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001898 LOOPER_END
1899}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001900
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001901void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001902 CHECK_SHADER_NOSETCONTEXT(paint);
1903
reed@google.com93645112012-07-26 16:11:47 +00001904 if (!path.isFinite()) {
1905 return;
1906 }
1907
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001908 SkRect storage;
1909 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001910 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001911 const SkRect& pathBounds = path.getBounds();
1912 bounds = &paint.computeFastBounds(pathBounds, &storage);
1913 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914 return;
1915 }
1916 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001917
1918 const SkRect& r = path.getBounds();
1919 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001920 if (path.isInverseFillType()) {
1921 this->internalDrawPaint(paint);
1922 }
1923 return;
1924 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001926 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927
1928 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001929 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930 }
1931
reed@google.com4e2b3d32011-04-07 14:18:59 +00001932 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933}
1934
1935void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1936 const SkPaint* paint) {
1937 SkDEBUGCODE(bitmap.validate();)
1938
reed@google.com3d608122011-11-21 15:16:16 +00001939 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001940 SkRect bounds = {
1941 x, y,
1942 x + SkIntToScalar(bitmap.width()),
1943 y + SkIntToScalar(bitmap.height())
1944 };
1945 if (paint) {
1946 (void)paint->computeFastBounds(bounds, &bounds);
1947 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001948 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949 return;
1950 }
1951 }
reed@google.com4b226022011-01-11 18:32:13 +00001952
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953 SkMatrix matrix;
1954 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001955 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956}
1957
reed@google.com9987ec32011-09-07 11:57:52 +00001958// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001959void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001960 const SkRect& dst, const SkPaint* paint,
1961 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001962 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963 return;
1964 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001965
reed@google.comea033602012-12-14 13:13:55 +00001966 CHECK_LOCKCOUNT_BALANCE(bitmap);
1967
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001968 SkRect storage;
1969 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001970 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001971 if (paint) {
1972 bounds = &paint->computeFastBounds(dst, &storage);
1973 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001974 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001975 return;
1976 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977 }
reed@google.com3d608122011-11-21 15:16:16 +00001978
reed@google.com33535f32012-09-25 15:37:50 +00001979 SkLazyPaint lazy;
1980 if (NULL == paint) {
1981 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001983
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001984 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001985
reed@google.com33535f32012-09-25 15:37:50 +00001986 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001987 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001988 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001989
reed@google.com33535f32012-09-25 15:37:50 +00001990 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991}
1992
reed@google.com71121732012-09-18 15:14:33 +00001993void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001994 const SkRect& dst, const SkPaint* paint,
1995 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001996 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001997 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001998}
1999
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2001 const SkPaint* paint) {
2002 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002003 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004}
2005
reed@google.com9987ec32011-09-07 11:57:52 +00002006void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2007 const SkIRect& center, const SkRect& dst,
2008 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002009 if (bitmap.drawsNothing()) {
2010 return;
2011 }
reed@google.com3d608122011-11-21 15:16:16 +00002012 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002013 SkRect storage;
2014 const SkRect* bounds = &dst;
2015 if (paint) {
2016 bounds = &paint->computeFastBounds(dst, &storage);
2017 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002018 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002019 return;
2020 }
2021 }
2022
reed@google.com9987ec32011-09-07 11:57:52 +00002023 const int32_t w = bitmap.width();
2024 const int32_t h = bitmap.height();
2025
2026 SkIRect c = center;
2027 // pin center to the bounds of the bitmap
2028 c.fLeft = SkMax32(0, center.fLeft);
2029 c.fTop = SkMax32(0, center.fTop);
2030 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2031 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2032
reed@google.com71121732012-09-18 15:14:33 +00002033 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002034 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002035 };
2036 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002037 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002038 };
reed@google.com9987ec32011-09-07 11:57:52 +00002039 SkScalar dstX[4] = {
2040 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2041 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2042 };
2043 SkScalar dstY[4] = {
2044 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2045 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2046 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002047
reed@google.com9987ec32011-09-07 11:57:52 +00002048 if (dstX[1] > dstX[2]) {
2049 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2050 dstX[2] = dstX[1];
2051 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002052
reed@google.com9987ec32011-09-07 11:57:52 +00002053 if (dstY[1] > dstY[2]) {
2054 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2055 dstY[2] = dstY[1];
2056 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002057
reed@google.com9987ec32011-09-07 11:57:52 +00002058 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002059 SkRect s, d;
2060
reed@google.com9987ec32011-09-07 11:57:52 +00002061 s.fTop = srcY[y];
2062 s.fBottom = srcY[y+1];
2063 d.fTop = dstY[y];
2064 d.fBottom = dstY[y+1];
2065 for (int x = 0; x < 3; x++) {
2066 s.fLeft = srcX[x];
2067 s.fRight = srcX[x+1];
2068 d.fLeft = dstX[x];
2069 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002070 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002071 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002072 }
2073 }
2074}
2075
2076void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2077 const SkRect& dst, const SkPaint* paint) {
2078 SkDEBUGCODE(bitmap.validate();)
2079
2080 // Need a device entry-point, so gpu can use a mesh
2081 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2082}
2083
reed@google.comf67e4cf2011-03-15 20:56:58 +00002084class SkDeviceFilteredPaint {
2085public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002086 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2087 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002088 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002089 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002090 newPaint->setFlags(flags.fFlags);
2091 newPaint->setHinting(flags.fHinting);
2092 fPaint = newPaint;
2093 } else {
2094 fPaint = &paint;
2095 }
2096 }
2097
reed@google.comf67e4cf2011-03-15 20:56:58 +00002098 const SkPaint& paint() const { return *fPaint; }
2099
2100private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002101 const SkPaint* fPaint;
2102 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002103};
2104
bungeman@google.com52c748b2011-08-22 21:30:43 +00002105void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2106 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002107 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002108 draw.fDevice->drawRect(draw, r, paint);
2109 } else {
2110 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002111 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002112 draw.fDevice->drawRect(draw, r, p);
2113 }
2114}
2115
2116void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2117 const char text[], size_t byteLength,
2118 SkScalar x, SkScalar y) {
2119 SkASSERT(byteLength == 0 || text != NULL);
2120
2121 // nothing to draw
2122 if (text == NULL || byteLength == 0 ||
2123 draw.fClip->isEmpty() ||
2124 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2125 return;
2126 }
2127
2128 SkScalar width = 0;
2129 SkPoint start;
2130
2131 start.set(0, 0); // to avoid warning
2132 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2133 SkPaint::kStrikeThruText_Flag)) {
2134 width = paint.measureText(text, byteLength);
2135
2136 SkScalar offsetX = 0;
2137 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2138 offsetX = SkScalarHalf(width);
2139 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2140 offsetX = width;
2141 }
2142 start.set(x - offsetX, y);
2143 }
2144
2145 if (0 == width) {
2146 return;
2147 }
2148
2149 uint32_t flags = paint.getFlags();
2150
2151 if (flags & (SkPaint::kUnderlineText_Flag |
2152 SkPaint::kStrikeThruText_Flag)) {
2153 SkScalar textSize = paint.getTextSize();
2154 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2155 SkRect r;
2156
2157 r.fLeft = start.fX;
2158 r.fRight = start.fX + width;
2159
2160 if (flags & SkPaint::kUnderlineText_Flag) {
2161 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2162 start.fY);
2163 r.fTop = offset;
2164 r.fBottom = offset + height;
2165 DrawRect(draw, paint, r, textSize);
2166 }
2167 if (flags & SkPaint::kStrikeThruText_Flag) {
2168 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2169 start.fY);
2170 r.fTop = offset;
2171 r.fBottom = offset + height;
2172 DrawRect(draw, paint, r, textSize);
2173 }
2174 }
2175}
2176
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177void SkCanvas::drawText(const void* text, size_t byteLength,
2178 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002179 CHECK_SHADER_NOSETCONTEXT(paint);
2180
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002181 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182
2183 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002184 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002185 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002186 DrawTextDecorations(iter, dfp.paint(),
2187 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 }
2189
reed@google.com4e2b3d32011-04-07 14:18:59 +00002190 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191}
2192
2193void SkCanvas::drawPosText(const void* text, size_t byteLength,
2194 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002195 CHECK_SHADER_NOSETCONTEXT(paint);
2196
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002197 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002198
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002200 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002202 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 }
reed@google.com4b226022011-01-11 18:32:13 +00002204
reed@google.com4e2b3d32011-04-07 14:18:59 +00002205 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206}
2207
2208void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2209 const SkScalar xpos[], SkScalar constY,
2210 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002211 CHECK_SHADER_NOSETCONTEXT(paint);
2212
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002213 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002214
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002216 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002218 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219 }
reed@google.com4b226022011-01-11 18:32:13 +00002220
reed@google.com4e2b3d32011-04-07 14:18:59 +00002221 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222}
2223
2224void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2225 const SkPath& path, const SkMatrix* matrix,
2226 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002227 CHECK_SHADER_NOSETCONTEXT(paint);
2228
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002229 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230
2231 while (iter.next()) {
2232 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002233 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002234 }
2235
reed@google.com4e2b3d32011-04-07 14:18:59 +00002236 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237}
2238
2239void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2240 const SkPoint verts[], const SkPoint texs[],
2241 const SkColor colors[], SkXfermode* xmode,
2242 const uint16_t indices[], int indexCount,
2243 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002244 CHECK_SHADER_NOSETCONTEXT(paint);
2245
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002246 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002247
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 while (iter.next()) {
2249 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002250 colors, xmode, indices, indexCount,
2251 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 }
reed@google.com4b226022011-01-11 18:32:13 +00002253
reed@google.com4e2b3d32011-04-07 14:18:59 +00002254 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255}
2256
2257//////////////////////////////////////////////////////////////////////////////
2258// These methods are NOT virtual, and therefore must call back into virtual
2259// methods, rather than actually drawing themselves.
2260//////////////////////////////////////////////////////////////////////////////
2261
2262void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002263 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 SkPaint paint;
2265
2266 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002267 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002268 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 }
2270 this->drawPaint(paint);
2271}
2272
reed@android.com845fdac2009-06-23 03:01:32 +00002273void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274 SkPaint paint;
2275
2276 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002277 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002278 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 }
2280 this->drawPaint(paint);
2281}
2282
2283void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2284 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002285
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 pt.set(x, y);
2287 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2288}
2289
2290void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2291 SkPoint pt;
2292 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002293
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294 pt.set(x, y);
2295 paint.setColor(color);
2296 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2297}
2298
2299void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2300 const SkPaint& paint) {
2301 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002302
reed@android.com8a1c16f2008-12-17 15:59:43 +00002303 pts[0].set(x0, y0);
2304 pts[1].set(x1, y1);
2305 this->drawPoints(kLines_PointMode, 2, pts, paint);
2306}
2307
2308void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2309 SkScalar right, SkScalar bottom,
2310 const SkPaint& paint) {
2311 SkRect r;
2312
2313 r.set(left, top, right, bottom);
2314 this->drawRect(r, paint);
2315}
2316
2317void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2318 const SkPaint& paint) {
2319 if (radius < 0) {
2320 radius = 0;
2321 }
2322
2323 SkRect r;
2324 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002325 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002326}
2327
2328void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2329 const SkPaint& paint) {
2330 if (rx > 0 && ry > 0) {
2331 if (paint.canComputeFastBounds()) {
2332 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002333 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334 return;
2335 }
2336 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002337 SkRRect rrect;
2338 rrect.setRectXY(r, rx, ry);
2339 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 } else {
2341 this->drawRect(r, paint);
2342 }
2343}
2344
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2346 SkScalar sweepAngle, bool useCenter,
2347 const SkPaint& paint) {
2348 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2349 this->drawOval(oval, paint);
2350 } else {
2351 SkPath path;
2352 if (useCenter) {
2353 path.moveTo(oval.centerX(), oval.centerY());
2354 }
2355 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2356 if (useCenter) {
2357 path.close();
2358 }
2359 this->drawPath(path, paint);
2360 }
2361}
2362
2363void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2364 const SkPath& path, SkScalar hOffset,
2365 SkScalar vOffset, const SkPaint& paint) {
2366 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002367
reed@android.com8a1c16f2008-12-17 15:59:43 +00002368 matrix.setTranslate(hOffset, vOffset);
2369 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2370}
2371
reed@android.comf76bacf2009-05-13 14:00:33 +00002372///////////////////////////////////////////////////////////////////////////////
2373
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002375 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376}
2377
reed@android.com8a1c16f2008-12-17 15:59:43 +00002378///////////////////////////////////////////////////////////////////////////////
2379///////////////////////////////////////////////////////////////////////////////
2380
2381SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002382 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383
2384 SkASSERT(canvas);
2385
2386 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2387 fDone = !fImpl->next();
2388}
2389
2390SkCanvas::LayerIter::~LayerIter() {
2391 fImpl->~SkDrawIter();
2392}
2393
2394void SkCanvas::LayerIter::next() {
2395 fDone = !fImpl->next();
2396}
2397
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002398SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 return fImpl->getDevice();
2400}
2401
2402const SkMatrix& SkCanvas::LayerIter::matrix() const {
2403 return fImpl->getMatrix();
2404}
2405
2406const SkPaint& SkCanvas::LayerIter::paint() const {
2407 const SkPaint* paint = fImpl->getPaint();
2408 if (NULL == paint) {
2409 paint = &fDefaultPaint;
2410 }
2411 return *paint;
2412}
2413
2414const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2415int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2416int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002417
2418///////////////////////////////////////////////////////////////////////////////
2419
2420SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002421
2422///////////////////////////////////////////////////////////////////////////////
2423
2424static bool supported_for_raster_canvas(const SkImageInfo& info) {
2425 switch (info.alphaType()) {
2426 case kPremul_SkAlphaType:
2427 case kOpaque_SkAlphaType:
2428 break;
2429 default:
2430 return false;
2431 }
2432
2433 switch (info.colorType()) {
2434 case kAlpha_8_SkColorType:
2435 case kRGB_565_SkColorType:
2436 case kPMColor_SkColorType:
2437 break;
2438 default:
2439 return false;
2440 }
2441
2442 return true;
2443}
2444
2445SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2446 if (!supported_for_raster_canvas(info)) {
2447 return NULL;
2448 }
2449
2450 SkBitmap bitmap;
2451 if (!bitmap.allocPixels(info)) {
2452 return NULL;
2453 }
2454
2455 // should this functionality be moved into allocPixels()?
2456 if (!bitmap.info().isOpaque()) {
2457 bitmap.eraseColor(0);
2458 }
2459 return SkNEW_ARGS(SkCanvas, (bitmap));
2460}