blob: 71b81a1fa2b4ae1b2d355a22e00379a3b3e76102 [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:
215 MCRec* fNext;
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000216 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
219 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 DeviceCM* fLayer;
222 /* If there are any layers in the stack, this points to the top-most
223 one that is at or below this level in the stack (so we know what
224 bitmap/device to draw into from this level. This value is NOT
225 reference counted, since the real owner is either our fLayer field,
226 or a previous one in a lower level.)
227 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000228 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000230 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if (NULL != prev) {
232 if (flags & SkCanvas::kMatrix_SaveFlag) {
233 fMatrixStorage = *prev->fMatrix;
234 fMatrix = &fMatrixStorage;
235 } else {
236 fMatrix = prev->fMatrix;
237 }
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000240 fRasterClipStorage = *prev->fRasterClip;
241 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 } else {
reed@google.com00177082011-10-12 14:34:30 +0000243 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
245
246 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000247 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 fTopLayer = prev->fTopLayer;
250 } else { // no prev
251 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000254 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = NULL;
256 fTopLayer = NULL;
257 }
258 fLayer = NULL;
259
260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000264 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDELETE(fLayer);
266 dec_rec();
267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
reed@google.com00177082011-10-12 14:34:30 +0000270 SkMatrix fMatrixStorage;
271 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
274class SkDrawIter : public SkDraw {
275public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000277 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000278 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 canvas->updateDeviceCMCache();
280
reed@google.com90c07ea2012-04-13 13:50:27 +0000281 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fBounder = canvas->getBounder();
283 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000284 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com4b226022011-01-11 18:32:13 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 bool next() {
288 // skip over recs with empty clips
289 if (fSkipEmptyClips) {
290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
291 fCurrLayer = fCurrLayer->fNext;
292 }
293 }
294
reed@google.comf68c5e22012-02-24 16:38:58 +0000295 const DeviceCM* rec = fCurrLayer;
296 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297
298 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
300 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 fDevice = rec->fDevice;
302 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000304 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 fCurrLayer = rec->fNext;
307 if (fBounder) {
308 fBounder->setClip(fClip);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313 }
314 return false;
315 }
reed@google.com4b226022011-01-11 18:32:13 +0000316
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000317 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000318 int getX() const { return fDevice->getOrigin().x(); }
319 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 const SkMatrix& getMatrix() const { return *fMatrix; }
321 const SkRegion& getClip() const { return *fClip; }
322 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324private:
325 SkCanvas* fCanvas;
326 const DeviceCM* fCurrLayer;
327 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 SkBool8 fSkipEmptyClips;
329
330 typedef SkDraw INHERITED;
331};
332
333/////////////////////////////////////////////////////////////////////////////
334
335class AutoDrawLooper {
336public:
reed@google.com8926b162012-03-23 15:36:36 +0000337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
338 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000339 fCanvas = canvas;
340 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000344 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
reed@google.com8926b162012-03-23 15:36:36 +0000347 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
348 SkPaint tmp;
349 tmp.setImageFilter(fOrigPaint.getImageFilter());
350 // it would be nice if we had a guess at the bounds, instead of null
351 (void)canvas->internalSaveLayer(NULL, &tmp,
352 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
353 // we'll clear the imageFilter for the actual draws in next(), so
354 // it will only be applied during the restore().
355 fDoClearImageFilter = true;
356 }
357
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 if (fLooper) {
359 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000360 fIsSimple = false;
361 } else {
362 // can we be marked as simple?
363 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364 }
365 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000368 if (fDoClearImageFilter) {
369 fCanvas->internalRestore();
370 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000371 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000373
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 const SkPaint& paint() const {
375 SkASSERT(fPaint);
376 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000378
reed@google.com129ec222012-05-15 13:24:09 +0000379 bool next(SkDrawFilter::Type drawType) {
380 if (fDone) {
381 return false;
382 } else if (fIsSimple) {
383 fDone = true;
384 fPaint = &fOrigPaint;
385 return !fPaint->nothingToDraw();
386 } else {
387 return this->doNext(drawType);
388 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000389 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000392 SkLazyPaint fLazyPaint;
393 SkCanvas* fCanvas;
394 const SkPaint& fOrigPaint;
395 SkDrawLooper* fLooper;
396 SkDrawFilter* fFilter;
397 const SkPaint* fPaint;
398 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000399 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000400 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000401 bool fIsSimple;
402
403 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404};
405
reed@google.com129ec222012-05-15 13:24:09 +0000406bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000407 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000408 SkASSERT(!fIsSimple);
409 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
410
411 SkPaint* paint = fLazyPaint.set(fOrigPaint);
412
413 if (fDoClearImageFilter) {
414 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416
reed@google.com129ec222012-05-15 13:24:09 +0000417 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000419 return false;
420 }
421 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000422 if (!fFilter->filter(paint, drawType)) {
423 fDone = true;
424 return false;
425 }
reed@google.com129ec222012-05-15 13:24:09 +0000426 if (NULL == fLooper) {
427 // no looper means we only draw once
428 fDone = true;
429 }
430 }
431 fPaint = paint;
432
433 // if we only came in here for the imagefilter, mark us as done
434 if (!fLooper && !fFilter) {
435 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000436 }
437
438 // call this after any possible paint modifiers
439 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440 fPaint = NULL;
441 return false;
442 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 return true;
444}
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446/* Stack helper for managing a SkBounder. In the destructor, if we were
447 given a bounder, we call its commit() method, signifying that we are
448 done accumulating bounds for that draw.
449*/
450class SkAutoBounderCommit {
451public:
452 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
453 ~SkAutoBounderCommit() {
454 if (NULL != fBounder) {
455 fBounder->commit();
456 }
457 }
458private:
459 SkBounder* fBounder;
460};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000461#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462
463#include "SkColorPriv.h"
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465////////// macros to place around the internal draw calls //////////////////
466
reed@google.com8926b162012-03-23 15:36:36 +0000467#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000468 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000469 AutoDrawLooper looper(this, paint, true); \
470 while (looper.next(type)) { \
471 SkAutoBounderCommit ac(fBounder); \
472 SkDrawIter iter(this);
473
reed@google.com4e2b3d32011-04-07 14:18:59 +0000474#define LOOPER_BEGIN(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000475 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000476 AutoDrawLooper looper(this, paint); \
477 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkAutoBounderCommit ac(fBounder); \
479 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000480
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482
483////////////////////////////////////////////////////////////////////////////
484
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000485SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000487 fCachedLocalClipBounds.setEmpty();
488 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000489 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000490 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000491 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000492 fSaveLayerCount = 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;
500 fMCRec->fNext = NULL;
501
reed@google.com97af1a62012-08-28 12:19:02 +0000502 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 return this->setDevice(device);
505}
506
reed@google.comcde92112011-07-06 20:00:52 +0000507SkCanvas::SkCanvas()
508: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000509 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000510
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000511 this->init(NULL);
512}
513
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000514SkCanvas::SkCanvas(SkBaseDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000515 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 inc_canvas();
517
518 this->init(device);
519}
520
521SkCanvas::SkCanvas(const SkBitmap& bitmap)
522 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
523 inc_canvas();
524
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000525 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526}
527
528SkCanvas::~SkCanvas() {
529 // free up the contents of our deque
530 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000531 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 this->internalRestore(); // restore the last, since we're going away
534
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000535 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000536 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 dec_canvas();
539}
540
541SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
542 SkRefCnt_SafeAssign(fBounder, bounder);
543 return bounder;
544}
545
546SkDrawFilter* SkCanvas::getDrawFilter() const {
547 return fMCRec->fFilter;
548}
549
550SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
551 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
552 return filter;
553}
554
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000555SkMetaData& SkCanvas::getMetaData() {
556 // metadata users are rare, so we lazily allocate it. If that changes we
557 // can decide to just make it a field in the device (rather than a ptr)
558 if (NULL == fMetaData) {
559 fMetaData = new SkMetaData;
560 }
561 return *fMetaData;
562}
563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564///////////////////////////////////////////////////////////////////////////////
565
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000566void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000567 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000568 if (device) {
569 device->flush();
570 }
571}
572
reed@google.com210ce002011-11-01 14:24:23 +0000573SkISize SkCanvas::getDeviceSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000574 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000575 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
576}
577
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000578SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000580 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 SkASSERT(rec && rec->fLayer);
582 return rec->fLayer->fDevice;
583}
584
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000585SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000586 if (updateMatrixClip) {
587 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
588 }
reed@google.com9266fed2011-03-30 00:18:03 +0000589 return fMCRec->fTopLayer->fDevice;
590}
591
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000592SkBaseDevice* SkCanvas::setDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000594 SkDeque::F2BIter iter(fMCStack);
595 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000597 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598
599 if (rootDevice == device) {
600 return device;
601 }
reed@google.com4b226022011-01-11 18:32:13 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000604 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 }
606 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000607 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 }
609
610 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
611 rootDevice = device;
612
613 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 /* Now we update our initial region to have the bounds of the new device,
616 and then intersect all of the clips in our stack with these bounds,
617 to ensure that we can't draw outside of the device's bounds (and trash
618 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 NOTE: this is only a partial-fix, since if the new device is larger than
621 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000622 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
624 reconstruct the correct clips, so this approximation will have to do.
625 The caller really needs to restore() back to the base if they want to
626 accurately take advantage of the new device bounds.
627 */
628
reed@google.com42aea282012-03-28 16:19:15 +0000629 SkIRect bounds;
630 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000632 } else {
633 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
reed@google.com42aea282012-03-28 16:19:15 +0000635 // now jam our 1st clip to be bounds, and intersect the rest with that
636 rec->fRasterClip->setRect(bounds);
637 while ((rec = (MCRec*)iter.next()) != NULL) {
638 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
639 }
640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 return device;
642}
643
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000644bool SkCanvas::readPixels(SkBitmap* bitmap,
645 int x, int y,
646 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000647 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000648 if (!device) {
649 return false;
650 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000651 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000652}
653
bsalomon@google.comc6980972011-11-02 19:57:21 +0000654bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000655 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000656 if (!device) {
657 return false;
658 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000659
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000660 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000661 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000662 if (!bounds.intersect(srcRect)) {
663 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000664 }
665
666 SkBitmap tmp;
667 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
668 bounds.height());
669 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
670 bitmap->swap(tmp);
671 return true;
672 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000673 return false;
674 }
reed@google.com51df9e32010-12-23 19:29:18 +0000675}
676
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000677void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
678 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000679 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000680 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000681 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
682 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
683 device->accessBitmap(true);
684 device->writePixels(bitmap, x, y, config8888);
685 }
reed@google.com51df9e32010-12-23 19:29:18 +0000686 }
687}
688
junov@google.com4370aed2012-01-18 16:21:08 +0000689SkCanvas* SkCanvas::canvasForDrawIter() {
690 return this;
691}
692
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693//////////////////////////////////////////////////////////////////////////////
694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695void SkCanvas::updateDeviceCMCache() {
696 if (fDeviceCMDirty) {
697 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000698 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000700
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000702 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000704 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000706 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 } while ((layer = layer->fNext) != NULL);
708 }
709 fDeviceCMDirty = false;
710 }
711}
712
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713///////////////////////////////////////////////////////////////////////////////
714
715int SkCanvas::internalSave(SaveFlags flags) {
716 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000717
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 MCRec* newTop = (MCRec*)fMCStack.push_back();
719 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000720
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 newTop->fNext = fMCRec;
722 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000723
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000724 if (SkCanvas::kClip_SaveFlag & flags) {
725 fClipStack.save();
726 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 return saveCount;
729}
730
731int SkCanvas::save(SaveFlags flags) {
732 // call shared impl
733 return this->internalSave(flags);
734}
735
736#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
737#define C16MASK (1 << SkBitmap::kRGB_565_Config)
738#define C8MASK (1 << SkBitmap::kA8_Config)
739
740static SkBitmap::Config resolve_config(SkCanvas* canvas,
741 const SkIRect& bounds,
742 SkCanvas::SaveFlags flags,
743 bool* isOpaque) {
744 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
745
746#if 0
747 // loop through and union all the configs we may draw into
748 uint32_t configMask = 0;
749 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
750 {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000751 SkBaseDevice* device = canvas->getLayerDevice(i);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 if (device->intersects(bounds))
753 configMask |= 1 << device->config();
754 }
755
756 // if the caller wants alpha or fullcolor, we can't return 565
757 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
758 SkCanvas::kHasAlphaLayer_SaveFlag))
759 configMask &= ~C16MASK;
760
761 switch (configMask) {
762 case C8MASK: // if we only have A8, return that
763 return SkBitmap::kA8_Config;
764
765 case C16MASK: // if we only have 565, return that
766 return SkBitmap::kRGB_565_Config;
767
768 default:
769 return SkBitmap::kARGB_8888_Config; // default answer
770 }
771#else
772 return SkBitmap::kARGB_8888_Config; // default answer
773#endif
774}
775
776static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
777 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
778}
779
junov@chromium.orga907ac32012-02-24 21:54:07 +0000780bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
781 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000782 SkIRect clipBounds;
783 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000784 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000785 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000786 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 if (NULL != bounds) {
788 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 this->getTotalMatrix().mapRect(&r, *bounds);
791 r.roundOut(&ir);
792 // early exit if the layer's bounds are clipped out
793 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000794 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000795 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000796 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000797 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 }
799 } else { // no user bounds, so just use the clip
800 ir = clipBounds;
801 }
802
reed@google.com5c3d1472011-02-22 19:12:23 +0000803 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000804
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 // early exit if the clip is now empty
806 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000807 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000808 return false;
809 }
810
811 if (intersection) {
812 *intersection = ir;
813 }
814 return true;
815}
816
817int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
818 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000819 return this->internalSaveLayer(bounds, paint, flags, false);
820}
821
822int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
823 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000824 // do this before we create the layer. We don't call the public save() since
825 // that would invoke a possibly overridden virtual
826 int count = this->internalSave(flags);
827
828 fDeviceCMDirty = true;
829
830 SkIRect ir;
831 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return count;
833 }
834
reed@google.comb55deeb2012-01-06 14:43:09 +0000835 // Kill the imagefilter if our device doesn't allow it
836 SkLazyPaint lazyP;
837 if (paint && paint->getImageFilter()) {
838 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000839 if (justForImageFilter) {
840 // early exit if the layer was just for the imageFilter
841 return count;
842 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000843 SkPaint* p = lazyP.set(*paint);
844 p->setImageFilter(NULL);
845 paint = p;
846 }
847 }
848
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 bool isOpaque;
850 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
851
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000852 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000853 if (paint && paint->getImageFilter()) {
854 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
855 isOpaque);
856 } else {
857 device = this->createLayerDevice(config, ir.width(), ir.height(),
858 isOpaque);
859 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000860 if (NULL == device) {
861 SkDebugf("Unable to create device for layer.");
862 return count;
863 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000864
reed@google.com6f8f2922011-03-04 22:27:10 +0000865 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000866 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 device->unref();
868
869 layer->fNext = fMCRec->fTopLayer;
870 fMCRec->fLayer = layer;
871 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
872
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000873 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 return count;
875}
876
877int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
878 SaveFlags flags) {
879 if (0xFF == alpha) {
880 return this->saveLayer(bounds, NULL, flags);
881 } else {
882 SkPaint tmpPaint;
883 tmpPaint.setAlpha(alpha);
884 return this->saveLayer(bounds, &tmpPaint, flags);
885 }
886}
887
888void SkCanvas::restore() {
889 // check for underflow
890 if (fMCStack.count() > 1) {
891 this->internalRestore();
892 }
893}
894
895void SkCanvas::internalRestore() {
896 SkASSERT(fMCStack.count() != 0);
897
898 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000899 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000901 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
902 fClipStack.restore();
903 }
904
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000905 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 DeviceCM* layer = fMCRec->fLayer; // may be null
907 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
908 fMCRec->fLayer = NULL;
909
910 // now do the normal restore()
911 fMCRec->~MCRec(); // balanced in save()
912 fMCStack.pop_back();
913 fMCRec = (MCRec*)fMCStack.back();
914
915 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
916 since if we're being recorded, we don't want to record this (the
917 recorder will have already recorded the restore).
918 */
919 if (NULL != layer) {
920 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000921 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000922 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
923 layer->fPaint);
924 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000926
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000927 SkASSERT(fSaveLayerCount > 0);
928 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 }
930 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000931 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932}
933
934int SkCanvas::getSaveCount() const {
935 return fMCStack.count();
936}
937
938void SkCanvas::restoreToCount(int count) {
939 // sanity check
940 if (count < 1) {
941 count = 1;
942 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000943
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000944 int n = this->getSaveCount() - count;
945 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 this->restore();
947 }
948}
949
reed@google.com7c202932011-12-14 18:48:05 +0000950bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000951 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000952}
953
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954/////////////////////////////////////////////////////////////////////////////
955
956// can't draw it if its empty, or its too big for a fixed-point width or height
957static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +0000958 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959}
960
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000961void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 const SkMatrix& matrix, const SkPaint* paint) {
963 if (reject_bitmap(bitmap)) {
964 return;
965 }
966
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000967 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000969 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000971
972 SkDEBUGCODE(bitmap.validate();)
973 CHECK_LOCKCOUNT_BALANCE(bitmap);
974
975 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
976
977 while (iter.next()) {
978 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
979 }
980
981 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982}
983
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000984void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +0000985 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 SkPaint tmp;
987 if (NULL == paint) {
988 tmp.setDither(true);
989 paint = &tmp;
990 }
reed@google.com4b226022011-01-11 18:32:13 +0000991
reed@google.com8926b162012-03-23 15:36:36 +0000992 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000994 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000995 paint = &looper.paint();
996 SkImageFilter* filter = paint->getImageFilter();
997 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000998 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000999 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001000 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001001 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001002 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001003 SkMatrix matrix = *iter.fMatrix;
1004 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001005 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001006 SkPaint tmpUnfiltered(*paint);
1007 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001008 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1009 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001010 }
1011 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001012 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001013 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001015 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016}
1017
reed@google.com8926b162012-03-23 15:36:36 +00001018void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1019 const SkPaint* paint) {
1020 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001021 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001022
reed@google.com8926b162012-03-23 15:36:36 +00001023 if (reject_bitmap(bitmap)) {
1024 return;
1025 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001026
reed@google.com8926b162012-03-23 15:36:36 +00001027 SkPaint tmp;
1028 if (NULL == paint) {
1029 paint = &tmp;
1030 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001031
reed@google.com8926b162012-03-23 15:36:36 +00001032 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001033
reed@google.com8926b162012-03-23 15:36:36 +00001034 while (iter.next()) {
1035 paint = &looper.paint();
1036 SkImageFilter* filter = paint->getImageFilter();
1037 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1038 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001039 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001040 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001041 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001042 SkMatrix matrix = *iter.fMatrix;
1043 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001044 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001045 SkPaint tmpUnfiltered(*paint);
1046 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001047 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001048 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001049 }
1050 } else {
1051 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1052 }
1053 }
1054 LOOPER_END
1055}
1056
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057/////////////////////////////////////////////////////////////////////////////
1058
1059bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1060 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001061 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 return fMCRec->fMatrix->preTranslate(dx, dy);
1063}
1064
1065bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1066 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001067 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 return fMCRec->fMatrix->preScale(sx, sy);
1069}
1070
1071bool SkCanvas::rotate(SkScalar degrees) {
1072 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001073 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 return fMCRec->fMatrix->preRotate(degrees);
1075}
1076
1077bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1078 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001079 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 return fMCRec->fMatrix->preSkew(sx, sy);
1081}
1082
1083bool SkCanvas::concat(const SkMatrix& matrix) {
1084 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001085 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 return fMCRec->fMatrix->preConcat(matrix);
1087}
1088
1089void SkCanvas::setMatrix(const SkMatrix& matrix) {
1090 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001091 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 *fMCRec->fMatrix = matrix;
1093}
1094
1095// this is not virtual, so it must call a virtual method so that subclasses
1096// will see its action
1097void SkCanvas::resetMatrix() {
1098 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001099
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 matrix.reset();
1101 this->setMatrix(matrix);
1102}
1103
1104//////////////////////////////////////////////////////////////////////////////
1105
reed@google.comc42d35d2011-10-12 11:57:42 +00001106bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001107#ifdef SK_ENABLE_CLIP_QUICKREJECT
1108 if (SkRegion::kIntersect_Op == op) {
1109 if (fMCRec->fRasterClip->isEmpty()) {
1110 return false;
1111 }
1112
reed@google.com3b3e8952012-08-16 20:53:31 +00001113 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001114 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001115 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001116
1117 fClipStack.clipEmpty();
1118 return fMCRec->fRasterClip->setEmpty();
1119 }
1120 }
1121#endif
1122
reed@google.com5c3d1472011-02-22 19:12:23 +00001123 AutoValidateClip avc(this);
1124
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001126 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001127 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128
1129 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001130 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001131 // the matrix. This means we don't have to a) make a path, and b) tell
1132 // the region code to scan-convert the path, only to discover that it
1133 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135
1136 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001137 fClipStack.clipDevRect(r, op, doAA);
1138 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001140 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001141 // and clip against that, since it can handle any matrix. However, to
1142 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1143 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 SkPath path;
1145
1146 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001147 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 }
1149}
1150
reed@google.com00177082011-10-12 14:34:30 +00001151static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001152 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001153 // base is used to limit the size (and therefore memory allocation) of the
1154 // region that results from scan converting devPath.
1155 SkRegion base;
1156
reed@google.com819c9212011-02-23 18:56:55 +00001157 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001158 // since we are intersect, we can do better (tighter) with currRgn's
1159 // bounds, than just using the device. However, if currRgn is complex,
1160 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001161 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001162 // FIXME: we should also be able to do this when currClip->isBW(),
1163 // but relaxing the test above triggers GM asserts in
1164 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1165 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001166 } else {
reed@google.com00177082011-10-12 14:34:30 +00001167 base.setRect(currClip->getBounds());
1168 SkRasterClip clip;
1169 clip.setPath(devPath, base, doAA);
1170 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001171 }
reed@google.com819c9212011-02-23 18:56:55 +00001172 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001173 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001174 if (!device) {
1175 return currClip->setEmpty();
1176 }
1177
junov@chromium.orga907ac32012-02-24 21:54:07 +00001178 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001179
1180 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001181 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001182 } else {
reed@google.com00177082011-10-12 14:34:30 +00001183 SkRasterClip clip;
1184 clip.setPath(devPath, base, doAA);
1185 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001186 }
1187 }
1188}
1189
reed@google.com4ed0fb72012-12-12 20:48:18 +00001190bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1191 if (rrect.isRect()) {
1192 // call the non-virtual version
1193 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1194 } else {
1195 SkPath path;
1196 path.addRRect(rrect);
1197 // call the non-virtual version
1198 return this->SkCanvas::clipPath(path, op, doAA);
1199 }
1200}
1201
reed@google.comc42d35d2011-10-12 11:57:42 +00001202bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001203#ifdef SK_ENABLE_CLIP_QUICKREJECT
1204 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1205 if (fMCRec->fRasterClip->isEmpty()) {
1206 return false;
1207 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001208
reed@google.com3b3e8952012-08-16 20:53:31 +00001209 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001210 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001211 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001212
reed@google.comda17f752012-08-16 18:27:05 +00001213 fClipStack.clipEmpty();
1214 return fMCRec->fRasterClip->setEmpty();
1215 }
1216 }
1217#endif
1218
reed@google.com5c3d1472011-02-22 19:12:23 +00001219 AutoValidateClip avc(this);
1220
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001222 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001223 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224
1225 SkPath devPath;
1226 path.transform(*fMCRec->fMatrix, &devPath);
1227
reed@google.comfe701122011-11-08 19:41:23 +00001228 // Check if the transfomation, or the original path itself
1229 // made us empty. Note this can also happen if we contained NaN
1230 // values. computing the bounds detects this, and will set our
1231 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1232 if (devPath.getBounds().isEmpty()) {
1233 // resetting the path will remove any NaN or other wanky values
1234 // that might upset our scan converter.
1235 devPath.reset();
1236 }
1237
reed@google.com5c3d1472011-02-22 19:12:23 +00001238 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001239 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001240
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001241 if (fAllowSimplifyClip) {
1242 devPath.reset();
1243 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1244 const SkClipStack* clipStack = getClipStack();
1245 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1246 const SkClipStack::Element* element;
1247 while ((element = iter.next())) {
1248 SkClipStack::Element::Type type = element->getType();
1249 if (type == SkClipStack::Element::kEmpty_Type) {
1250 continue;
1251 }
1252 SkPath operand;
1253 if (type == SkClipStack::Element::kRect_Type) {
1254 operand.addRect(element->getRect());
1255 } else if (type == SkClipStack::Element::kPath_Type) {
1256 operand = element->getPath();
1257 } else {
1258 SkDEBUGFAIL("Unexpected type.");
1259 }
1260 SkRegion::Op elementOp = element->getOp();
1261 if (elementOp == SkRegion::kReplace_Op) {
1262 devPath = operand;
1263 } else {
1264 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1265 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001266 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1267 // perhaps we need an API change to avoid this sort of mixed-signals about
1268 // clipping.
1269 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001270 }
1271 op = SkRegion::kReplace_Op;
1272 }
1273
reed@google.com00177082011-10-12 14:34:30 +00001274 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275}
1276
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001277bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1278 bool inverseFilled) {
1279 // This is for updating the clip conservatively using only bounds
1280 // information.
1281 // Contract:
1282 // The current clip must contain the true clip. The true
1283 // clip is the clip that would have normally been computed
1284 // by calls to clipPath and clipRRect
1285 // Objective:
1286 // Keep the current clip as small as possible without
1287 // breaking the contract, using only clip bounding rectangles
1288 // (for performance).
1289
1290 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1291 // don't have to worry about getting caught in a loop. Thus anywhere
1292 // we call a virtual method, we explicitly prefix it with
1293 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001294
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001295 if (inverseFilled) {
1296 switch (op) {
1297 case SkRegion::kIntersect_Op:
1298 case SkRegion::kDifference_Op:
1299 // These ops can only shrink the current clip. So leaving
1300 // the clip unchanges conservatively respects the contract.
1301 return this->getClipDeviceBounds(NULL);
1302 case SkRegion::kUnion_Op:
1303 case SkRegion::kReplace_Op:
1304 case SkRegion::kReverseDifference_Op:
1305 case SkRegion::kXOR_Op:
1306 {
1307 // These ops can grow the current clip up to the extents of
1308 // the input clip, which is inverse filled, so we just set
1309 // the current clip to the device bounds.
1310 SkRect deviceBounds;
1311 SkIRect deviceIBounds;
1312 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001313 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001314 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1315 // set the clip in device space
1316 this->SkCanvas::setMatrix(SkMatrix::I());
1317 bool result = this->SkCanvas::clipRect(deviceBounds,
1318 SkRegion::kReplace_Op, false);
1319 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1320 return result;
1321 }
1322 default:
1323 SkASSERT(0); // unhandled op?
1324 }
1325 } else {
1326 // Not inverse filled
1327 switch (op) {
1328 case SkRegion::kIntersect_Op:
1329 case SkRegion::kUnion_Op:
1330 case SkRegion::kReplace_Op:
1331 return this->SkCanvas::clipRect(bounds, op, false);
1332 case SkRegion::kDifference_Op:
1333 // Difference can only shrink the current clip.
1334 // Leaving clip unchanged conservatively fullfills the contract.
1335 return this->getClipDeviceBounds(NULL);
1336 case SkRegion::kReverseDifference_Op:
1337 // To reverse, we swap in the bounds with a replace op.
1338 // As with difference, leave it unchanged.
1339 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1340 case SkRegion::kXOR_Op:
1341 // Be conservative, based on (A XOR B) always included in (A union B),
1342 // which is always included in (bounds(A) union bounds(B))
1343 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1344 default:
1345 SkASSERT(0); // unhandled op?
1346 }
1347 }
1348 return true;
1349}
1350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001352 AutoValidateClip avc(this);
1353
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001355 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356
reed@google.com5c3d1472011-02-22 19:12:23 +00001357 // todo: signal fClipStack that we have a region, and therefore (I guess)
1358 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001359 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001360
reed@google.com00177082011-10-12 14:34:30 +00001361 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362}
1363
reed@google.com819c9212011-02-23 18:56:55 +00001364#ifdef SK_DEBUG
1365void SkCanvas::validateClip() const {
1366 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001367 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001368 if (!device) {
1369 SkASSERT(this->getTotalClip().isEmpty());
1370 return;
1371 }
1372
reed@google.com819c9212011-02-23 18:56:55 +00001373 SkIRect ir;
1374 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001375 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001376
robertphillips@google.com80214e22012-07-20 15:33:18 +00001377 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001378 const SkClipStack::Element* element;
1379 while ((element = iter.next()) != NULL) {
1380 switch (element->getType()) {
1381 case SkClipStack::Element::kPath_Type:
1382 clipPathHelper(this,
1383 &tmpClip,
1384 element->getPath(),
1385 element->getOp(),
1386 element->isAA());
1387 break;
1388 case SkClipStack::Element::kRect_Type:
1389 element->getRect().round(&ir);
1390 tmpClip.op(ir, element->getOp());
1391 break;
1392 case SkClipStack::Element::kEmpty_Type:
1393 tmpClip.setEmpty();
1394 break;
reed@google.com819c9212011-02-23 18:56:55 +00001395 }
1396 }
1397
reed@google.com6f8f2922011-03-04 22:27:10 +00001398#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001399 // now compare against the current rgn
1400 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001401 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001402#endif
reed@google.com819c9212011-02-23 18:56:55 +00001403}
1404#endif
1405
reed@google.com90c07ea2012-04-13 13:50:27 +00001406void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001407 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001408 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001409
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001410 static const SkRect kEmpty = { 0, 0, 0, 0 };
1411 while ((element = iter.next()) != NULL) {
1412 switch (element->getType()) {
1413 case SkClipStack::Element::kPath_Type:
1414 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1415 break;
1416 case SkClipStack::Element::kRect_Type:
1417 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1418 break;
1419 case SkClipStack::Element::kEmpty_Type:
1420 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1421 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001422 }
1423 }
1424}
1425
reed@google.com5c3d1472011-02-22 19:12:23 +00001426///////////////////////////////////////////////////////////////////////////////
1427
reed@google.com3b3e8952012-08-16 20:53:31 +00001428bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001429
reed@google.com16078632011-12-06 18:56:37 +00001430 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001431 return true;
1432
reed@google.com00177082011-10-12 14:34:30 +00001433 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434 return true;
1435 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436
tomhudson@google.com8d430182011-06-06 19:11:19 +00001437 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001438 SkRect dst;
1439 fMCRec->fMatrix->mapRect(&dst, rect);
1440 SkIRect idst;
1441 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001442 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001443 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001444 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001445
reed@android.coma380ae42009-07-21 01:17:02 +00001446 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001447 // TODO: should we use | instead, or compare all 4 at once?
1448 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001449 return true;
1450 }
reed@google.comc0784db2013-12-13 21:16:12 +00001451 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001452 return true;
1453 }
1454 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
reed@google.com3b3e8952012-08-16 20:53:31 +00001458bool SkCanvas::quickReject(const SkPath& path) const {
1459 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460}
1461
reed@google.com3b3e8952012-08-16 20:53:31 +00001462bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001463 SkIRect ibounds;
1464 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 return false;
1466 }
1467
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001468 SkMatrix inverse;
1469 // if we can't invert the CTM, we can't return local clip bounds
1470 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001471 if (bounds) {
1472 bounds->setEmpty();
1473 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001474 return false;
1475 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001477 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001478 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001479 // adjust it outwards in case we are antialiasing
1480 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001481
reed@google.com8f4d2302013-12-17 16:44:46 +00001482 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1483 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 inverse.mapRect(bounds, r);
1485 }
1486 return true;
1487}
1488
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001489bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001490 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001491 if (clip.isEmpty()) {
1492 if (bounds) {
1493 bounds->setEmpty();
1494 }
1495 return false;
1496 }
1497
1498 if (NULL != bounds) {
1499 *bounds = clip.getBounds();
1500 }
1501 return true;
1502}
1503
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504const SkMatrix& SkCanvas::getTotalMatrix() const {
1505 return *fMCRec->fMatrix;
1506}
1507
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001508SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001509 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1510 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001511 return kComplex_ClipType;
1512}
1513
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001515 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516}
1517
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001518SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001519 int width, int height,
1520 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001521 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001522 if (device) {
1523 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1524 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001525 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001526 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001527 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528}
1529
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001530SkBaseDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001531 int width, int height,
1532 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001533 SkBaseDevice* device = this->getDevice();
bsalomon@google.come97f0852011-06-17 13:10:25 +00001534 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001535 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001536 } else {
1537 return NULL;
1538 }
1539}
1540
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001541GrContext* SkCanvas::getGrContext() {
1542#if SK_SUPPORT_GPU
1543 SkBaseDevice* device = this->getTopDevice();
1544 if (NULL != device) {
1545 GrRenderTarget* renderTarget = device->accessRenderTarget();
1546 if (NULL != renderTarget) {
1547 return renderTarget->getContext();
1548 }
1549 }
1550#endif
1551
1552 return NULL;
1553
1554}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001555
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556//////////////////////////////////////////////////////////////////////////////
1557// These are the virtual drawing methods
1558//////////////////////////////////////////////////////////////////////////////
1559
reed@google.com2a981812011-04-14 18:59:28 +00001560void SkCanvas::clear(SkColor color) {
1561 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001562 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001563 while (iter.next()) {
1564 iter.fDevice->clear(color);
1565 }
1566}
1567
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001569 this->internalDrawPaint(paint);
1570}
1571
1572void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001573 CHECK_SHADER_NOSETCONTEXT(paint);
1574
reed@google.com4e2b3d32011-04-07 14:18:59 +00001575 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576
1577 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001578 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 }
1580
reed@google.com4e2b3d32011-04-07 14:18:59 +00001581 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582}
1583
1584void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1585 const SkPaint& paint) {
1586 if ((long)count <= 0) {
1587 return;
1588 }
1589
reed@google.comea033602012-12-14 13:13:55 +00001590 CHECK_SHADER_NOSETCONTEXT(paint);
1591
reed@google.coma584aed2012-05-16 14:06:02 +00001592 if (paint.canComputeFastBounds()) {
1593 SkRect r;
1594 // special-case 2 points (common for drawing a single line)
1595 if (2 == count) {
1596 r.set(pts[0], pts[1]);
1597 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001598 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001599 }
1600 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001601 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001602 return;
1603 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001604 }
reed@google.coma584aed2012-05-16 14:06:02 +00001605
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606 SkASSERT(pts != NULL);
1607
reed@google.com4e2b3d32011-04-07 14:18:59 +00001608 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001609
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001611 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612 }
reed@google.com4b226022011-01-11 18:32:13 +00001613
reed@google.com4e2b3d32011-04-07 14:18:59 +00001614 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615}
1616
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001617void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001618 CHECK_SHADER_NOSETCONTEXT(paint);
1619
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 if (paint.canComputeFastBounds()) {
1621 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001622 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623 return;
1624 }
1625 }
reed@google.com4b226022011-01-11 18:32:13 +00001626
reed@google.com4e2b3d32011-04-07 14:18:59 +00001627 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628
1629 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001630 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631 }
1632
reed@google.com4e2b3d32011-04-07 14:18:59 +00001633 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634}
1635
reed@google.com4ed0fb72012-12-12 20:48:18 +00001636void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001637 CHECK_SHADER_NOSETCONTEXT(paint);
1638
reed@google.com4ed0fb72012-12-12 20:48:18 +00001639 if (paint.canComputeFastBounds()) {
1640 SkRect storage;
1641 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1642 return;
1643 }
1644 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001645
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001646 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1647
1648 while (iter.next()) {
1649 iter.fDevice->drawOval(iter, oval, looper.paint());
1650 }
1651
1652 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001653}
1654
1655void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001656 CHECK_SHADER_NOSETCONTEXT(paint);
1657
reed@google.com4ed0fb72012-12-12 20:48:18 +00001658 if (paint.canComputeFastBounds()) {
1659 SkRect storage;
1660 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1661 return;
1662 }
1663 }
1664
1665 if (rrect.isRect()) {
1666 // call the non-virtual version
1667 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001668 return;
1669 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001670 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001671 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1672 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001673 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001674
1675 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1676
1677 while (iter.next()) {
1678 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1679 }
1680
1681 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001682}
1683
1684
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001685void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001686 CHECK_SHADER_NOSETCONTEXT(paint);
1687
reed@google.com93645112012-07-26 16:11:47 +00001688 if (!path.isFinite()) {
1689 return;
1690 }
1691
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001692 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001693 SkRect storage;
1694 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001695 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001696 return;
1697 }
1698 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001699 if (path.isEmpty()) {
1700 if (path.isInverseFillType()) {
1701 this->internalDrawPaint(paint);
1702 }
1703 return;
1704 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705
reed@google.com4e2b3d32011-04-07 14:18:59 +00001706 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707
1708 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001709 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710 }
1711
reed@google.com4e2b3d32011-04-07 14:18:59 +00001712 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713}
1714
1715void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1716 const SkPaint* paint) {
1717 SkDEBUGCODE(bitmap.validate();)
1718
reed@google.com3d608122011-11-21 15:16:16 +00001719 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001720 SkRect bounds = {
1721 x, y,
1722 x + SkIntToScalar(bitmap.width()),
1723 y + SkIntToScalar(bitmap.height())
1724 };
1725 if (paint) {
1726 (void)paint->computeFastBounds(bounds, &bounds);
1727 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001728 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729 return;
1730 }
1731 }
reed@google.com4b226022011-01-11 18:32:13 +00001732
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733 SkMatrix matrix;
1734 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001735 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736}
1737
reed@google.com9987ec32011-09-07 11:57:52 +00001738// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001739void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001740 const SkRect& dst, const SkPaint* paint,
1741 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1743 return;
1744 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001745
reed@google.comea033602012-12-14 13:13:55 +00001746 CHECK_LOCKCOUNT_BALANCE(bitmap);
1747
reed@google.com3d608122011-11-21 15:16:16 +00001748 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001749 SkRect storage;
1750 const SkRect* bounds = &dst;
1751 if (paint) {
1752 bounds = &paint->computeFastBounds(dst, &storage);
1753 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001754 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001755 return;
1756 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 }
reed@google.com3d608122011-11-21 15:16:16 +00001758
reed@google.com33535f32012-09-25 15:37:50 +00001759 SkLazyPaint lazy;
1760 if (NULL == paint) {
1761 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001763
reed@google.com33535f32012-09-25 15:37:50 +00001764 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001765
reed@google.com33535f32012-09-25 15:37:50 +00001766 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001767 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001768 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001769
reed@google.com33535f32012-09-25 15:37:50 +00001770 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771}
1772
reed@google.com71121732012-09-18 15:14:33 +00001773void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001774 const SkRect& dst, const SkPaint* paint,
1775 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001776 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001777 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001778}
1779
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1781 const SkPaint* paint) {
1782 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001783 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784}
1785
reed@google.com9987ec32011-09-07 11:57:52 +00001786void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1787 const SkIRect& center, const SkRect& dst,
1788 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001789 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001790 SkRect storage;
1791 const SkRect* bounds = &dst;
1792 if (paint) {
1793 bounds = &paint->computeFastBounds(dst, &storage);
1794 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001795 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001796 return;
1797 }
1798 }
1799
reed@google.com9987ec32011-09-07 11:57:52 +00001800 const int32_t w = bitmap.width();
1801 const int32_t h = bitmap.height();
1802
1803 SkIRect c = center;
1804 // pin center to the bounds of the bitmap
1805 c.fLeft = SkMax32(0, center.fLeft);
1806 c.fTop = SkMax32(0, center.fTop);
1807 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1808 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1809
reed@google.com71121732012-09-18 15:14:33 +00001810 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001811 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001812 };
1813 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001814 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001815 };
reed@google.com9987ec32011-09-07 11:57:52 +00001816 SkScalar dstX[4] = {
1817 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1818 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1819 };
1820 SkScalar dstY[4] = {
1821 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1822 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1823 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001824
reed@google.com9987ec32011-09-07 11:57:52 +00001825 if (dstX[1] > dstX[2]) {
1826 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1827 dstX[2] = dstX[1];
1828 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001829
reed@google.com9987ec32011-09-07 11:57:52 +00001830 if (dstY[1] > dstY[2]) {
1831 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1832 dstY[2] = dstY[1];
1833 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001834
reed@google.com9987ec32011-09-07 11:57:52 +00001835 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001836 SkRect s, d;
1837
reed@google.com9987ec32011-09-07 11:57:52 +00001838 s.fTop = srcY[y];
1839 s.fBottom = srcY[y+1];
1840 d.fTop = dstY[y];
1841 d.fBottom = dstY[y+1];
1842 for (int x = 0; x < 3; x++) {
1843 s.fLeft = srcX[x];
1844 s.fRight = srcX[x+1];
1845 d.fLeft = dstX[x];
1846 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001847 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001848 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001849 }
1850 }
1851}
1852
1853void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1854 const SkRect& dst, const SkPaint* paint) {
1855 SkDEBUGCODE(bitmap.validate();)
1856
1857 // Need a device entry-point, so gpu can use a mesh
1858 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1859}
1860
reed@google.comf67e4cf2011-03-15 20:56:58 +00001861class SkDeviceFilteredPaint {
1862public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001863 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1864 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001865 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001866 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001867 newPaint->setFlags(flags.fFlags);
1868 newPaint->setHinting(flags.fHinting);
1869 fPaint = newPaint;
1870 } else {
1871 fPaint = &paint;
1872 }
1873 }
1874
reed@google.comf67e4cf2011-03-15 20:56:58 +00001875 const SkPaint& paint() const { return *fPaint; }
1876
1877private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001878 const SkPaint* fPaint;
1879 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001880};
1881
bungeman@google.com52c748b2011-08-22 21:30:43 +00001882void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1883 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001884 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001885 draw.fDevice->drawRect(draw, r, paint);
1886 } else {
1887 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001888 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001889 draw.fDevice->drawRect(draw, r, p);
1890 }
1891}
1892
1893void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1894 const char text[], size_t byteLength,
1895 SkScalar x, SkScalar y) {
1896 SkASSERT(byteLength == 0 || text != NULL);
1897
1898 // nothing to draw
1899 if (text == NULL || byteLength == 0 ||
1900 draw.fClip->isEmpty() ||
1901 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1902 return;
1903 }
1904
1905 SkScalar width = 0;
1906 SkPoint start;
1907
1908 start.set(0, 0); // to avoid warning
1909 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1910 SkPaint::kStrikeThruText_Flag)) {
1911 width = paint.measureText(text, byteLength);
1912
1913 SkScalar offsetX = 0;
1914 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1915 offsetX = SkScalarHalf(width);
1916 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1917 offsetX = width;
1918 }
1919 start.set(x - offsetX, y);
1920 }
1921
1922 if (0 == width) {
1923 return;
1924 }
1925
1926 uint32_t flags = paint.getFlags();
1927
1928 if (flags & (SkPaint::kUnderlineText_Flag |
1929 SkPaint::kStrikeThruText_Flag)) {
1930 SkScalar textSize = paint.getTextSize();
1931 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1932 SkRect r;
1933
1934 r.fLeft = start.fX;
1935 r.fRight = start.fX + width;
1936
1937 if (flags & SkPaint::kUnderlineText_Flag) {
1938 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1939 start.fY);
1940 r.fTop = offset;
1941 r.fBottom = offset + height;
1942 DrawRect(draw, paint, r, textSize);
1943 }
1944 if (flags & SkPaint::kStrikeThruText_Flag) {
1945 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1946 start.fY);
1947 r.fTop = offset;
1948 r.fBottom = offset + height;
1949 DrawRect(draw, paint, r, textSize);
1950 }
1951 }
1952}
1953
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954void SkCanvas::drawText(const void* text, size_t byteLength,
1955 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001956 CHECK_SHADER_NOSETCONTEXT(paint);
1957
reed@google.com4e2b3d32011-04-07 14:18:59 +00001958 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959
1960 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001961 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001962 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001963 DrawTextDecorations(iter, dfp.paint(),
1964 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965 }
1966
reed@google.com4e2b3d32011-04-07 14:18:59 +00001967 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968}
1969
1970void SkCanvas::drawPosText(const void* text, size_t byteLength,
1971 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001972 CHECK_SHADER_NOSETCONTEXT(paint);
1973
reed@google.com4e2b3d32011-04-07 14:18:59 +00001974 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001975
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001977 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001979 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980 }
reed@google.com4b226022011-01-11 18:32:13 +00001981
reed@google.com4e2b3d32011-04-07 14:18:59 +00001982 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983}
1984
1985void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1986 const SkScalar xpos[], SkScalar constY,
1987 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001988 CHECK_SHADER_NOSETCONTEXT(paint);
1989
reed@google.com4e2b3d32011-04-07 14:18:59 +00001990 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001991
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001993 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001994 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001995 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 }
reed@google.com4b226022011-01-11 18:32:13 +00001997
reed@google.com4e2b3d32011-04-07 14:18:59 +00001998 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999}
2000
2001void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2002 const SkPath& path, const SkMatrix* matrix,
2003 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002004 CHECK_SHADER_NOSETCONTEXT(paint);
2005
reed@google.com4e2b3d32011-04-07 14:18:59 +00002006 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007
2008 while (iter.next()) {
2009 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002010 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011 }
2012
reed@google.com4e2b3d32011-04-07 14:18:59 +00002013 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014}
2015
2016void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2017 const SkPoint verts[], const SkPoint texs[],
2018 const SkColor colors[], SkXfermode* xmode,
2019 const uint16_t indices[], int indexCount,
2020 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002021 CHECK_SHADER_NOSETCONTEXT(paint);
2022
reed@google.com4e2b3d32011-04-07 14:18:59 +00002023 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002024
reed@android.com8a1c16f2008-12-17 15:59:43 +00002025 while (iter.next()) {
2026 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002027 colors, xmode, indices, indexCount,
2028 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002029 }
reed@google.com4b226022011-01-11 18:32:13 +00002030
reed@google.com4e2b3d32011-04-07 14:18:59 +00002031 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032}
2033
2034//////////////////////////////////////////////////////////////////////////////
2035// These methods are NOT virtual, and therefore must call back into virtual
2036// methods, rather than actually drawing themselves.
2037//////////////////////////////////////////////////////////////////////////////
2038
2039void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002040 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002041 SkPaint paint;
2042
2043 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002044 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002045 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002046 }
2047 this->drawPaint(paint);
2048}
2049
reed@android.com845fdac2009-06-23 03:01:32 +00002050void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051 SkPaint paint;
2052
2053 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002054 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002055 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056 }
2057 this->drawPaint(paint);
2058}
2059
2060void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2061 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002062
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063 pt.set(x, y);
2064 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2065}
2066
2067void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2068 SkPoint pt;
2069 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002070
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071 pt.set(x, y);
2072 paint.setColor(color);
2073 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2074}
2075
2076void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2077 const SkPaint& paint) {
2078 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002079
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080 pts[0].set(x0, y0);
2081 pts[1].set(x1, y1);
2082 this->drawPoints(kLines_PointMode, 2, pts, paint);
2083}
2084
2085void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2086 SkScalar right, SkScalar bottom,
2087 const SkPaint& paint) {
2088 SkRect r;
2089
2090 r.set(left, top, right, bottom);
2091 this->drawRect(r, paint);
2092}
2093
2094void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2095 const SkPaint& paint) {
2096 if (radius < 0) {
2097 radius = 0;
2098 }
2099
2100 SkRect r;
2101 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002102 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103}
2104
2105void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2106 const SkPaint& paint) {
2107 if (rx > 0 && ry > 0) {
2108 if (paint.canComputeFastBounds()) {
2109 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002110 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 return;
2112 }
2113 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002114 SkRRect rrect;
2115 rrect.setRectXY(r, rx, ry);
2116 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 } else {
2118 this->drawRect(r, paint);
2119 }
2120}
2121
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2123 SkScalar sweepAngle, bool useCenter,
2124 const SkPaint& paint) {
2125 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2126 this->drawOval(oval, paint);
2127 } else {
2128 SkPath path;
2129 if (useCenter) {
2130 path.moveTo(oval.centerX(), oval.centerY());
2131 }
2132 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2133 if (useCenter) {
2134 path.close();
2135 }
2136 this->drawPath(path, paint);
2137 }
2138}
2139
2140void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2141 const SkPath& path, SkScalar hOffset,
2142 SkScalar vOffset, const SkPaint& paint) {
2143 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002144
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145 matrix.setTranslate(hOffset, vOffset);
2146 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2147}
2148
reed@android.comf76bacf2009-05-13 14:00:33 +00002149///////////////////////////////////////////////////////////////////////////////
2150
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153}
2154
reed@android.com8a1c16f2008-12-17 15:59:43 +00002155///////////////////////////////////////////////////////////////////////////////
2156///////////////////////////////////////////////////////////////////////////////
2157
2158SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002159 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160
2161 SkASSERT(canvas);
2162
2163 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2164 fDone = !fImpl->next();
2165}
2166
2167SkCanvas::LayerIter::~LayerIter() {
2168 fImpl->~SkDrawIter();
2169}
2170
2171void SkCanvas::LayerIter::next() {
2172 fDone = !fImpl->next();
2173}
2174
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002175SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176 return fImpl->getDevice();
2177}
2178
2179const SkMatrix& SkCanvas::LayerIter::matrix() const {
2180 return fImpl->getMatrix();
2181}
2182
2183const SkPaint& SkCanvas::LayerIter::paint() const {
2184 const SkPaint* paint = fImpl->getPaint();
2185 if (NULL == paint) {
2186 paint = &fDefaultPaint;
2187 }
2188 return *paint;
2189}
2190
2191const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2192int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2193int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002194
2195///////////////////////////////////////////////////////////////////////////////
2196
2197SkCanvas::ClipVisitor::~ClipVisitor() { }