blob: 58ec8fd4c5bd0bc2c2db4dc10e2e6863379af128 [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) {
1529 SkASSERT(this->getTotalClip().isEmpty());
1530 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 }
1556
reed@google.com6f8f2922011-03-04 22:27:10 +00001557#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001558 // now compare against the current rgn
1559 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001560 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001561#endif
reed@google.com819c9212011-02-23 18:56:55 +00001562}
1563#endif
1564
reed@google.com90c07ea2012-04-13 13:50:27 +00001565void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001566 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001567 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001568
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001569 static const SkRect kEmpty = { 0, 0, 0, 0 };
1570 while ((element = iter.next()) != NULL) {
1571 switch (element->getType()) {
1572 case SkClipStack::Element::kPath_Type:
1573 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1574 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001575 case SkClipStack::Element::kRRect_Type:
1576 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1577 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001578 case SkClipStack::Element::kRect_Type:
1579 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1580 break;
1581 case SkClipStack::Element::kEmpty_Type:
1582 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1583 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001584 }
1585 }
1586}
1587
reed@google.com5c3d1472011-02-22 19:12:23 +00001588///////////////////////////////////////////////////////////////////////////////
1589
reed@google.com754de5f2014-02-24 19:38:20 +00001590bool SkCanvas::isClipEmpty() const {
1591 return fMCRec->fRasterClip->isEmpty();
1592}
1593
reed@google.com3b3e8952012-08-16 20:53:31 +00001594bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001595
reed@google.com16078632011-12-06 18:56:37 +00001596 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001597 return true;
1598
reed@google.com00177082011-10-12 14:34:30 +00001599 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600 return true;
1601 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602
tomhudson@google.com8d430182011-06-06 19:11:19 +00001603 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001604 SkRect dst;
1605 fMCRec->fMatrix->mapRect(&dst, rect);
1606 SkIRect idst;
1607 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001608 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001609 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001610 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001611
reed@android.coma380ae42009-07-21 01:17:02 +00001612 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001613 // TODO: should we use | instead, or compare all 4 at once?
1614 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001615 return true;
1616 }
reed@google.comc0784db2013-12-13 21:16:12 +00001617 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001618 return true;
1619 }
1620 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
reed@google.com3b3e8952012-08-16 20:53:31 +00001624bool SkCanvas::quickReject(const SkPath& path) const {
1625 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626}
1627
reed@google.com3b3e8952012-08-16 20:53:31 +00001628bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001629 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001630 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631 return false;
1632 }
1633
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001634 SkMatrix inverse;
1635 // if we can't invert the CTM, we can't return local clip bounds
1636 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001637 if (bounds) {
1638 bounds->setEmpty();
1639 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001640 return false;
1641 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001643 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001644 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001645 // adjust it outwards in case we are antialiasing
1646 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001647
reed@google.com8f4d2302013-12-17 16:44:46 +00001648 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1649 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650 inverse.mapRect(bounds, r);
1651 }
1652 return true;
1653}
1654
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001655bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001656 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001657 if (clip.isEmpty()) {
1658 if (bounds) {
1659 bounds->setEmpty();
1660 }
1661 return false;
1662 }
1663
1664 if (NULL != bounds) {
1665 *bounds = clip.getBounds();
1666 }
1667 return true;
1668}
1669
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670const SkMatrix& SkCanvas::getTotalMatrix() const {
1671 return *fMCRec->fMatrix;
1672}
1673
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001674SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001675 if (fMCRec->fRasterClip->isEmpty()) {
1676 return kEmpty_ClipType;
1677 }
1678 if (fMCRec->fRasterClip->isRect()) {
1679 return kRect_ClipType;
1680 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001681 return kComplex_ClipType;
1682}
1683
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001685 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686}
1687
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001688SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001689 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001690 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691}
1692
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001693GrContext* SkCanvas::getGrContext() {
1694#if SK_SUPPORT_GPU
1695 SkBaseDevice* device = this->getTopDevice();
1696 if (NULL != device) {
1697 GrRenderTarget* renderTarget = device->accessRenderTarget();
1698 if (NULL != renderTarget) {
1699 return renderTarget->getContext();
1700 }
1701 }
1702#endif
1703
1704 return NULL;
1705
1706}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001707
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001708void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1709 const SkPaint& paint) {
1710 if (outer.isEmpty()) {
1711 return;
1712 }
1713 if (inner.isEmpty()) {
1714 this->drawRRect(outer, paint);
1715 return;
1716 }
1717
1718 // We don't have this method (yet), but technically this is what we should
1719 // be able to assert...
1720 // SkASSERT(outer.contains(inner));
1721 //
1722 // For now at least check for containment of bounds
1723 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1724
1725 this->onDrawDRRect(outer, inner, paint);
1726}
1727
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728//////////////////////////////////////////////////////////////////////////////
1729// These are the virtual drawing methods
1730//////////////////////////////////////////////////////////////////////////////
1731
reed@google.com2a981812011-04-14 18:59:28 +00001732void SkCanvas::clear(SkColor color) {
1733 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001734 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001735 while (iter.next()) {
1736 iter.fDevice->clear(color);
1737 }
1738}
1739
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001741 this->internalDrawPaint(paint);
1742}
1743
1744void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001745 CHECK_SHADER_NOSETCONTEXT(paint);
1746
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001747 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748
1749 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001750 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 }
1752
reed@google.com4e2b3d32011-04-07 14:18:59 +00001753 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754}
1755
1756void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1757 const SkPaint& paint) {
1758 if ((long)count <= 0) {
1759 return;
1760 }
1761
reed@google.comea033602012-12-14 13:13:55 +00001762 CHECK_SHADER_NOSETCONTEXT(paint);
1763
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001764 SkRect r, storage;
1765 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001766 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001767 // special-case 2 points (common for drawing a single line)
1768 if (2 == count) {
1769 r.set(pts[0], pts[1]);
1770 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001771 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001772 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001773 bounds = &paint.computeFastStrokeBounds(r, &storage);
1774 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001775 return;
1776 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001777 }
reed@google.coma584aed2012-05-16 14:06:02 +00001778
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 SkASSERT(pts != NULL);
1780
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001781 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001782
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001784 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 }
reed@google.com4b226022011-01-11 18:32:13 +00001786
reed@google.com4e2b3d32011-04-07 14:18:59 +00001787 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788}
1789
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001790void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001791 CHECK_SHADER_NOSETCONTEXT(paint);
1792
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001793 SkRect storage;
1794 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 bounds = &paint.computeFastBounds(r, &storage);
1797 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 return;
1799 }
1800 }
reed@google.com4b226022011-01-11 18:32:13 +00001801
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001802 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803
1804 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806 }
1807
reed@google.com4e2b3d32011-04-07 14:18:59 +00001808 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809}
1810
reed@google.com4ed0fb72012-12-12 20:48:18 +00001811void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001812 CHECK_SHADER_NOSETCONTEXT(paint);
1813
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001814 SkRect storage;
1815 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001816 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001817 bounds = &paint.computeFastBounds(oval, &storage);
1818 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001819 return;
1820 }
1821 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001822
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001824
1825 while (iter.next()) {
1826 iter.fDevice->drawOval(iter, oval, looper.paint());
1827 }
1828
1829 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001830}
1831
1832void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001833 CHECK_SHADER_NOSETCONTEXT(paint);
1834
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001835 SkRect storage;
1836 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001837 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001838 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1839 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001840 return;
1841 }
1842 }
1843
1844 if (rrect.isRect()) {
1845 // call the non-virtual version
1846 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001847 return;
1848 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001849 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001850 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1851 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001852 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001853
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001854 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001855
1856 while (iter.next()) {
1857 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1858 }
1859
1860 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001861}
1862
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001863void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1864 const SkPaint& paint) {
1865 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001866
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001867 SkRect storage;
1868 const SkRect* bounds = NULL;
1869 if (paint.canComputeFastBounds()) {
1870 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1871 if (this->quickReject(*bounds)) {
1872 return;
1873 }
1874 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001875
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001876 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001877
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001878 while (iter.next()) {
1879 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1880 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001881
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001882 LOOPER_END
1883}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001884
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001885void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001886 CHECK_SHADER_NOSETCONTEXT(paint);
1887
reed@google.com93645112012-07-26 16:11:47 +00001888 if (!path.isFinite()) {
1889 return;
1890 }
1891
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001892 SkRect storage;
1893 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001894 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001895 const SkRect& pathBounds = path.getBounds();
1896 bounds = &paint.computeFastBounds(pathBounds, &storage);
1897 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898 return;
1899 }
1900 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001901
1902 const SkRect& r = path.getBounds();
1903 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001904 if (path.isInverseFillType()) {
1905 this->internalDrawPaint(paint);
1906 }
1907 return;
1908 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001910 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911
1912 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001913 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914 }
1915
reed@google.com4e2b3d32011-04-07 14:18:59 +00001916 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917}
1918
1919void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1920 const SkPaint* paint) {
1921 SkDEBUGCODE(bitmap.validate();)
1922
reed@google.com3d608122011-11-21 15:16:16 +00001923 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001924 SkRect bounds = {
1925 x, y,
1926 x + SkIntToScalar(bitmap.width()),
1927 y + SkIntToScalar(bitmap.height())
1928 };
1929 if (paint) {
1930 (void)paint->computeFastBounds(bounds, &bounds);
1931 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001932 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 return;
1934 }
1935 }
reed@google.com4b226022011-01-11 18:32:13 +00001936
reed@android.com8a1c16f2008-12-17 15:59:43 +00001937 SkMatrix matrix;
1938 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001939 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940}
1941
reed@google.com9987ec32011-09-07 11:57:52 +00001942// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001943void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001944 const SkRect& dst, const SkPaint* paint,
1945 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001946 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 return;
1948 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001949
reed@google.comea033602012-12-14 13:13:55 +00001950 CHECK_LOCKCOUNT_BALANCE(bitmap);
1951
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001952 SkRect storage;
1953 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001954 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001955 if (paint) {
1956 bounds = &paint->computeFastBounds(dst, &storage);
1957 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001958 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001959 return;
1960 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001961 }
reed@google.com3d608122011-11-21 15:16:16 +00001962
reed@google.com33535f32012-09-25 15:37:50 +00001963 SkLazyPaint lazy;
1964 if (NULL == paint) {
1965 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001967
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001968 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001969
reed@google.com33535f32012-09-25 15:37:50 +00001970 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001971 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001972 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001973
reed@google.com33535f32012-09-25 15:37:50 +00001974 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975}
1976
reed@google.com71121732012-09-18 15:14:33 +00001977void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001978 const SkRect& dst, const SkPaint* paint,
1979 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001980 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001981 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001982}
1983
reed@android.com8a1c16f2008-12-17 15:59:43 +00001984void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1985 const SkPaint* paint) {
1986 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001987 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001988}
1989
reed@google.com9987ec32011-09-07 11:57:52 +00001990void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1991 const SkIRect& center, const SkRect& dst,
1992 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001993 if (bitmap.drawsNothing()) {
1994 return;
1995 }
reed@google.com3d608122011-11-21 15:16:16 +00001996 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001997 SkRect storage;
1998 const SkRect* bounds = &dst;
1999 if (paint) {
2000 bounds = &paint->computeFastBounds(dst, &storage);
2001 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002002 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002003 return;
2004 }
2005 }
2006
reed@google.com9987ec32011-09-07 11:57:52 +00002007 const int32_t w = bitmap.width();
2008 const int32_t h = bitmap.height();
2009
2010 SkIRect c = center;
2011 // pin center to the bounds of the bitmap
2012 c.fLeft = SkMax32(0, center.fLeft);
2013 c.fTop = SkMax32(0, center.fTop);
2014 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2015 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2016
reed@google.com71121732012-09-18 15:14:33 +00002017 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002018 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002019 };
2020 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002021 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002022 };
reed@google.com9987ec32011-09-07 11:57:52 +00002023 SkScalar dstX[4] = {
2024 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2025 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2026 };
2027 SkScalar dstY[4] = {
2028 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2029 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2030 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002031
reed@google.com9987ec32011-09-07 11:57:52 +00002032 if (dstX[1] > dstX[2]) {
2033 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2034 dstX[2] = dstX[1];
2035 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002036
reed@google.com9987ec32011-09-07 11:57:52 +00002037 if (dstY[1] > dstY[2]) {
2038 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2039 dstY[2] = dstY[1];
2040 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002041
reed@google.com9987ec32011-09-07 11:57:52 +00002042 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002043 SkRect s, d;
2044
reed@google.com9987ec32011-09-07 11:57:52 +00002045 s.fTop = srcY[y];
2046 s.fBottom = srcY[y+1];
2047 d.fTop = dstY[y];
2048 d.fBottom = dstY[y+1];
2049 for (int x = 0; x < 3; x++) {
2050 s.fLeft = srcX[x];
2051 s.fRight = srcX[x+1];
2052 d.fLeft = dstX[x];
2053 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002054 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002055 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002056 }
2057 }
2058}
2059
2060void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2061 const SkRect& dst, const SkPaint* paint) {
2062 SkDEBUGCODE(bitmap.validate();)
2063
2064 // Need a device entry-point, so gpu can use a mesh
2065 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2066}
2067
reed@google.comf67e4cf2011-03-15 20:56:58 +00002068class SkDeviceFilteredPaint {
2069public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002070 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2071 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002072 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002073 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002074 newPaint->setFlags(flags.fFlags);
2075 newPaint->setHinting(flags.fHinting);
2076 fPaint = newPaint;
2077 } else {
2078 fPaint = &paint;
2079 }
2080 }
2081
reed@google.comf67e4cf2011-03-15 20:56:58 +00002082 const SkPaint& paint() const { return *fPaint; }
2083
2084private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002085 const SkPaint* fPaint;
2086 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002087};
2088
bungeman@google.com52c748b2011-08-22 21:30:43 +00002089void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2090 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002091 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002092 draw.fDevice->drawRect(draw, r, paint);
2093 } else {
2094 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002095 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002096 draw.fDevice->drawRect(draw, r, p);
2097 }
2098}
2099
2100void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2101 const char text[], size_t byteLength,
2102 SkScalar x, SkScalar y) {
2103 SkASSERT(byteLength == 0 || text != NULL);
2104
2105 // nothing to draw
2106 if (text == NULL || byteLength == 0 ||
2107 draw.fClip->isEmpty() ||
2108 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2109 return;
2110 }
2111
2112 SkScalar width = 0;
2113 SkPoint start;
2114
2115 start.set(0, 0); // to avoid warning
2116 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2117 SkPaint::kStrikeThruText_Flag)) {
2118 width = paint.measureText(text, byteLength);
2119
2120 SkScalar offsetX = 0;
2121 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2122 offsetX = SkScalarHalf(width);
2123 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2124 offsetX = width;
2125 }
2126 start.set(x - offsetX, y);
2127 }
2128
2129 if (0 == width) {
2130 return;
2131 }
2132
2133 uint32_t flags = paint.getFlags();
2134
2135 if (flags & (SkPaint::kUnderlineText_Flag |
2136 SkPaint::kStrikeThruText_Flag)) {
2137 SkScalar textSize = paint.getTextSize();
2138 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2139 SkRect r;
2140
2141 r.fLeft = start.fX;
2142 r.fRight = start.fX + width;
2143
2144 if (flags & SkPaint::kUnderlineText_Flag) {
2145 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2146 start.fY);
2147 r.fTop = offset;
2148 r.fBottom = offset + height;
2149 DrawRect(draw, paint, r, textSize);
2150 }
2151 if (flags & SkPaint::kStrikeThruText_Flag) {
2152 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2153 start.fY);
2154 r.fTop = offset;
2155 r.fBottom = offset + height;
2156 DrawRect(draw, paint, r, textSize);
2157 }
2158 }
2159}
2160
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161void SkCanvas::drawText(const void* text, size_t byteLength,
2162 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002163 CHECK_SHADER_NOSETCONTEXT(paint);
2164
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002165 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166
2167 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002168 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002169 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002170 DrawTextDecorations(iter, dfp.paint(),
2171 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 }
2173
reed@google.com4e2b3d32011-04-07 14:18:59 +00002174 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175}
2176
2177void SkCanvas::drawPosText(const void* text, size_t byteLength,
2178 const SkPoint pos[], 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@google.com4b226022011-01-11 18:32:13 +00002182
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002184 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002186 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187 }
reed@google.com4b226022011-01-11 18:32:13 +00002188
reed@google.com4e2b3d32011-04-07 14:18:59 +00002189 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190}
2191
2192void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2193 const SkScalar xpos[], SkScalar constY,
2194 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, xpos, constY, 1,
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::drawTextOnPath(const void* text, size_t byteLength,
2209 const SkPath& path, const SkMatrix* matrix,
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@android.com8a1c16f2008-12-17 15:59:43 +00002214
2215 while (iter.next()) {
2216 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002217 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218 }
2219
reed@google.com4e2b3d32011-04-07 14:18:59 +00002220 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221}
2222
2223void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2224 const SkPoint verts[], const SkPoint texs[],
2225 const SkColor colors[], SkXfermode* xmode,
2226 const uint16_t indices[], int indexCount,
2227 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002228 CHECK_SHADER_NOSETCONTEXT(paint);
2229
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002230 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002231
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232 while (iter.next()) {
2233 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002234 colors, xmode, indices, indexCount,
2235 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002236 }
reed@google.com4b226022011-01-11 18:32:13 +00002237
reed@google.com4e2b3d32011-04-07 14:18:59 +00002238 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239}
2240
2241//////////////////////////////////////////////////////////////////////////////
2242// These methods are NOT virtual, and therefore must call back into virtual
2243// methods, rather than actually drawing themselves.
2244//////////////////////////////////////////////////////////////////////////////
2245
2246void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002247 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 SkPaint paint;
2249
2250 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002251 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002252 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253 }
2254 this->drawPaint(paint);
2255}
2256
reed@android.com845fdac2009-06-23 03:01:32 +00002257void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 SkPaint paint;
2259
2260 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002261 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002262 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263 }
2264 this->drawPaint(paint);
2265}
2266
2267void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2268 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002269
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 pt.set(x, y);
2271 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2272}
2273
2274void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2275 SkPoint pt;
2276 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002277
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278 pt.set(x, y);
2279 paint.setColor(color);
2280 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2281}
2282
2283void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2284 const SkPaint& paint) {
2285 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002286
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 pts[0].set(x0, y0);
2288 pts[1].set(x1, y1);
2289 this->drawPoints(kLines_PointMode, 2, pts, paint);
2290}
2291
2292void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2293 SkScalar right, SkScalar bottom,
2294 const SkPaint& paint) {
2295 SkRect r;
2296
2297 r.set(left, top, right, bottom);
2298 this->drawRect(r, paint);
2299}
2300
2301void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2302 const SkPaint& paint) {
2303 if (radius < 0) {
2304 radius = 0;
2305 }
2306
2307 SkRect r;
2308 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002309 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310}
2311
2312void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2313 const SkPaint& paint) {
2314 if (rx > 0 && ry > 0) {
2315 if (paint.canComputeFastBounds()) {
2316 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002317 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002318 return;
2319 }
2320 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002321 SkRRect rrect;
2322 rrect.setRectXY(r, rx, ry);
2323 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002324 } else {
2325 this->drawRect(r, paint);
2326 }
2327}
2328
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2330 SkScalar sweepAngle, bool useCenter,
2331 const SkPaint& paint) {
2332 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2333 this->drawOval(oval, paint);
2334 } else {
2335 SkPath path;
2336 if (useCenter) {
2337 path.moveTo(oval.centerX(), oval.centerY());
2338 }
2339 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2340 if (useCenter) {
2341 path.close();
2342 }
2343 this->drawPath(path, paint);
2344 }
2345}
2346
2347void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2348 const SkPath& path, SkScalar hOffset,
2349 SkScalar vOffset, const SkPaint& paint) {
2350 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002351
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 matrix.setTranslate(hOffset, vOffset);
2353 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2354}
2355
reed@android.comf76bacf2009-05-13 14:00:33 +00002356///////////////////////////////////////////////////////////////////////////////
2357
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360}
2361
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362///////////////////////////////////////////////////////////////////////////////
2363///////////////////////////////////////////////////////////////////////////////
2364
2365SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002366 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367
2368 SkASSERT(canvas);
2369
2370 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2371 fDone = !fImpl->next();
2372}
2373
2374SkCanvas::LayerIter::~LayerIter() {
2375 fImpl->~SkDrawIter();
2376}
2377
2378void SkCanvas::LayerIter::next() {
2379 fDone = !fImpl->next();
2380}
2381
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002382SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383 return fImpl->getDevice();
2384}
2385
2386const SkMatrix& SkCanvas::LayerIter::matrix() const {
2387 return fImpl->getMatrix();
2388}
2389
2390const SkPaint& SkCanvas::LayerIter::paint() const {
2391 const SkPaint* paint = fImpl->getPaint();
2392 if (NULL == paint) {
2393 paint = &fDefaultPaint;
2394 }
2395 return *paint;
2396}
2397
2398const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2399int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2400int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002401
2402///////////////////////////////////////////////////////////////////////////////
2403
2404SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002405
2406///////////////////////////////////////////////////////////////////////////////
2407
2408static bool supported_for_raster_canvas(const SkImageInfo& info) {
2409 switch (info.alphaType()) {
2410 case kPremul_SkAlphaType:
2411 case kOpaque_SkAlphaType:
2412 break;
2413 default:
2414 return false;
2415 }
2416
2417 switch (info.colorType()) {
2418 case kAlpha_8_SkColorType:
2419 case kRGB_565_SkColorType:
2420 case kPMColor_SkColorType:
2421 break;
2422 default:
2423 return false;
2424 }
2425
2426 return true;
2427}
2428
2429SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2430 if (!supported_for_raster_canvas(info)) {
2431 return NULL;
2432 }
2433
2434 SkBitmap bitmap;
2435 if (!bitmap.allocPixels(info)) {
2436 return NULL;
2437 }
2438
2439 // should this functionality be moved into allocPixels()?
2440 if (!bitmap.info().isOpaque()) {
2441 bitmap.eraseColor(0);
2442 }
2443 return SkNEW_ARGS(SkCanvas, (bitmap));
2444}