blob: feeba758fa3592bb1200116af3ef65d760af249a [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;
reed@google.comb55deeb2012-01-06 14:43:09 +00001001 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001002 SkMatrix matrix = *iter.fMatrix;
1003 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1004 if (filter->filterImage(&proxy, src, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001005 SkPaint tmpUnfiltered(*paint);
1006 tmpUnfiltered.setImageFilter(NULL);
1007 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001008 }
1009 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001010 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001011 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001013 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014}
1015
reed@google.com8926b162012-03-23 15:36:36 +00001016void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1017 const SkPaint* paint) {
1018 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001019 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001020
reed@google.com8926b162012-03-23 15:36:36 +00001021 if (reject_bitmap(bitmap)) {
1022 return;
1023 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001024
reed@google.com8926b162012-03-23 15:36:36 +00001025 SkPaint tmp;
1026 if (NULL == paint) {
1027 paint = &tmp;
1028 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001029
reed@google.com8926b162012-03-23 15:36:36 +00001030 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001031
reed@google.com8926b162012-03-23 15:36:36 +00001032 while (iter.next()) {
1033 paint = &looper.paint();
1034 SkImageFilter* filter = paint->getImageFilter();
1035 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1036 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001037 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001038 SkBitmap dst;
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001039 SkMatrix matrix = *iter.fMatrix;
1040 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1041 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001042 SkPaint tmpUnfiltered(*paint);
1043 tmpUnfiltered.setImageFilter(NULL);
1044 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1045 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001046 }
1047 } else {
1048 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1049 }
1050 }
1051 LOOPER_END
1052}
1053
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054/////////////////////////////////////////////////////////////////////////////
1055
1056bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1057 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001058 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 return fMCRec->fMatrix->preTranslate(dx, dy);
1060}
1061
1062bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1063 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001064 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 return fMCRec->fMatrix->preScale(sx, sy);
1066}
1067
1068bool SkCanvas::rotate(SkScalar degrees) {
1069 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001070 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 return fMCRec->fMatrix->preRotate(degrees);
1072}
1073
1074bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1075 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001076 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 return fMCRec->fMatrix->preSkew(sx, sy);
1078}
1079
1080bool SkCanvas::concat(const SkMatrix& matrix) {
1081 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001082 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 return fMCRec->fMatrix->preConcat(matrix);
1084}
1085
1086void SkCanvas::setMatrix(const SkMatrix& matrix) {
1087 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001088 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 *fMCRec->fMatrix = matrix;
1090}
1091
1092// this is not virtual, so it must call a virtual method so that subclasses
1093// will see its action
1094void SkCanvas::resetMatrix() {
1095 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001096
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 matrix.reset();
1098 this->setMatrix(matrix);
1099}
1100
1101//////////////////////////////////////////////////////////////////////////////
1102
reed@google.comc42d35d2011-10-12 11:57:42 +00001103bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001104#ifdef SK_ENABLE_CLIP_QUICKREJECT
1105 if (SkRegion::kIntersect_Op == op) {
1106 if (fMCRec->fRasterClip->isEmpty()) {
1107 return false;
1108 }
1109
reed@google.com3b3e8952012-08-16 20:53:31 +00001110 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001111 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001112 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001113
1114 fClipStack.clipEmpty();
1115 return fMCRec->fRasterClip->setEmpty();
1116 }
1117 }
1118#endif
1119
reed@google.com5c3d1472011-02-22 19:12:23 +00001120 AutoValidateClip avc(this);
1121
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001123 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001124 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125
1126 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001127 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001128 // the matrix. This means we don't have to a) make a path, and b) tell
1129 // the region code to scan-convert the path, only to discover that it
1130 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132
1133 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001134 fClipStack.clipDevRect(r, op, doAA);
1135 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001137 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001138 // and clip against that, since it can handle any matrix. However, to
1139 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1140 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 SkPath path;
1142
1143 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001144 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 }
1146}
1147
reed@google.com00177082011-10-12 14:34:30 +00001148static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001149 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001150 // base is used to limit the size (and therefore memory allocation) of the
1151 // region that results from scan converting devPath.
1152 SkRegion base;
1153
reed@google.com819c9212011-02-23 18:56:55 +00001154 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001155 // since we are intersect, we can do better (tighter) with currRgn's
1156 // bounds, than just using the device. However, if currRgn is complex,
1157 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001158 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001159 // FIXME: we should also be able to do this when currClip->isBW(),
1160 // but relaxing the test above triggers GM asserts in
1161 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1162 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001163 } else {
reed@google.com00177082011-10-12 14:34:30 +00001164 base.setRect(currClip->getBounds());
1165 SkRasterClip clip;
1166 clip.setPath(devPath, base, doAA);
1167 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001168 }
reed@google.com819c9212011-02-23 18:56:55 +00001169 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001170 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001171 if (!device) {
1172 return currClip->setEmpty();
1173 }
1174
junov@chromium.orga907ac32012-02-24 21:54:07 +00001175 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001176
1177 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001178 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001179 } else {
reed@google.com00177082011-10-12 14:34:30 +00001180 SkRasterClip clip;
1181 clip.setPath(devPath, base, doAA);
1182 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001183 }
1184 }
1185}
1186
reed@google.com4ed0fb72012-12-12 20:48:18 +00001187bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1188 if (rrect.isRect()) {
1189 // call the non-virtual version
1190 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1191 } else {
1192 SkPath path;
1193 path.addRRect(rrect);
1194 // call the non-virtual version
1195 return this->SkCanvas::clipPath(path, op, doAA);
1196 }
1197}
1198
reed@google.comc42d35d2011-10-12 11:57:42 +00001199bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001200#ifdef SK_ENABLE_CLIP_QUICKREJECT
1201 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1202 if (fMCRec->fRasterClip->isEmpty()) {
1203 return false;
1204 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001205
reed@google.com3b3e8952012-08-16 20:53:31 +00001206 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001207 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001208 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001209
reed@google.comda17f752012-08-16 18:27:05 +00001210 fClipStack.clipEmpty();
1211 return fMCRec->fRasterClip->setEmpty();
1212 }
1213 }
1214#endif
1215
reed@google.com5c3d1472011-02-22 19:12:23 +00001216 AutoValidateClip avc(this);
1217
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001219 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001220 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221
1222 SkPath devPath;
1223 path.transform(*fMCRec->fMatrix, &devPath);
1224
reed@google.comfe701122011-11-08 19:41:23 +00001225 // Check if the transfomation, or the original path itself
1226 // made us empty. Note this can also happen if we contained NaN
1227 // values. computing the bounds detects this, and will set our
1228 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1229 if (devPath.getBounds().isEmpty()) {
1230 // resetting the path will remove any NaN or other wanky values
1231 // that might upset our scan converter.
1232 devPath.reset();
1233 }
1234
reed@google.com5c3d1472011-02-22 19:12:23 +00001235 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001236 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001237
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001238 if (fAllowSimplifyClip) {
1239 devPath.reset();
1240 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1241 const SkClipStack* clipStack = getClipStack();
1242 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1243 const SkClipStack::Element* element;
1244 while ((element = iter.next())) {
1245 SkClipStack::Element::Type type = element->getType();
1246 if (type == SkClipStack::Element::kEmpty_Type) {
1247 continue;
1248 }
1249 SkPath operand;
1250 if (type == SkClipStack::Element::kRect_Type) {
1251 operand.addRect(element->getRect());
1252 } else if (type == SkClipStack::Element::kPath_Type) {
1253 operand = element->getPath();
1254 } else {
1255 SkDEBUGFAIL("Unexpected type.");
1256 }
1257 SkRegion::Op elementOp = element->getOp();
1258 if (elementOp == SkRegion::kReplace_Op) {
1259 devPath = operand;
1260 } else {
1261 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1262 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001263 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1264 // perhaps we need an API change to avoid this sort of mixed-signals about
1265 // clipping.
1266 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001267 }
1268 op = SkRegion::kReplace_Op;
1269 }
1270
reed@google.com00177082011-10-12 14:34:30 +00001271 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272}
1273
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001274bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1275 bool inverseFilled) {
1276 // This is for updating the clip conservatively using only bounds
1277 // information.
1278 // Contract:
1279 // The current clip must contain the true clip. The true
1280 // clip is the clip that would have normally been computed
1281 // by calls to clipPath and clipRRect
1282 // Objective:
1283 // Keep the current clip as small as possible without
1284 // breaking the contract, using only clip bounding rectangles
1285 // (for performance).
1286
1287 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1288 // don't have to worry about getting caught in a loop. Thus anywhere
1289 // we call a virtual method, we explicitly prefix it with
1290 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001291
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001292 if (inverseFilled) {
1293 switch (op) {
1294 case SkRegion::kIntersect_Op:
1295 case SkRegion::kDifference_Op:
1296 // These ops can only shrink the current clip. So leaving
1297 // the clip unchanges conservatively respects the contract.
1298 return this->getClipDeviceBounds(NULL);
1299 case SkRegion::kUnion_Op:
1300 case SkRegion::kReplace_Op:
1301 case SkRegion::kReverseDifference_Op:
1302 case SkRegion::kXOR_Op:
1303 {
1304 // These ops can grow the current clip up to the extents of
1305 // the input clip, which is inverse filled, so we just set
1306 // the current clip to the device bounds.
1307 SkRect deviceBounds;
1308 SkIRect deviceIBounds;
1309 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001310 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001311 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1312 // set the clip in device space
1313 this->SkCanvas::setMatrix(SkMatrix::I());
1314 bool result = this->SkCanvas::clipRect(deviceBounds,
1315 SkRegion::kReplace_Op, false);
1316 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1317 return result;
1318 }
1319 default:
1320 SkASSERT(0); // unhandled op?
1321 }
1322 } else {
1323 // Not inverse filled
1324 switch (op) {
1325 case SkRegion::kIntersect_Op:
1326 case SkRegion::kUnion_Op:
1327 case SkRegion::kReplace_Op:
1328 return this->SkCanvas::clipRect(bounds, op, false);
1329 case SkRegion::kDifference_Op:
1330 // Difference can only shrink the current clip.
1331 // Leaving clip unchanged conservatively fullfills the contract.
1332 return this->getClipDeviceBounds(NULL);
1333 case SkRegion::kReverseDifference_Op:
1334 // To reverse, we swap in the bounds with a replace op.
1335 // As with difference, leave it unchanged.
1336 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1337 case SkRegion::kXOR_Op:
1338 // Be conservative, based on (A XOR B) always included in (A union B),
1339 // which is always included in (bounds(A) union bounds(B))
1340 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1341 default:
1342 SkASSERT(0); // unhandled op?
1343 }
1344 }
1345 return true;
1346}
1347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001349 AutoValidateClip avc(this);
1350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001352 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353
reed@google.com5c3d1472011-02-22 19:12:23 +00001354 // todo: signal fClipStack that we have a region, and therefore (I guess)
1355 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001356 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001357
reed@google.com00177082011-10-12 14:34:30 +00001358 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359}
1360
reed@google.com819c9212011-02-23 18:56:55 +00001361#ifdef SK_DEBUG
1362void SkCanvas::validateClip() const {
1363 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001364 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001365 if (!device) {
1366 SkASSERT(this->getTotalClip().isEmpty());
1367 return;
1368 }
1369
reed@google.com819c9212011-02-23 18:56:55 +00001370 SkIRect ir;
1371 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001372 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001373
robertphillips@google.com80214e22012-07-20 15:33:18 +00001374 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001375 const SkClipStack::Element* element;
1376 while ((element = iter.next()) != NULL) {
1377 switch (element->getType()) {
1378 case SkClipStack::Element::kPath_Type:
1379 clipPathHelper(this,
1380 &tmpClip,
1381 element->getPath(),
1382 element->getOp(),
1383 element->isAA());
1384 break;
1385 case SkClipStack::Element::kRect_Type:
1386 element->getRect().round(&ir);
1387 tmpClip.op(ir, element->getOp());
1388 break;
1389 case SkClipStack::Element::kEmpty_Type:
1390 tmpClip.setEmpty();
1391 break;
reed@google.com819c9212011-02-23 18:56:55 +00001392 }
1393 }
1394
reed@google.com6f8f2922011-03-04 22:27:10 +00001395#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001396 // now compare against the current rgn
1397 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001398 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001399#endif
reed@google.com819c9212011-02-23 18:56:55 +00001400}
1401#endif
1402
reed@google.com90c07ea2012-04-13 13:50:27 +00001403void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001404 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001405 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001406
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001407 static const SkRect kEmpty = { 0, 0, 0, 0 };
1408 while ((element = iter.next()) != NULL) {
1409 switch (element->getType()) {
1410 case SkClipStack::Element::kPath_Type:
1411 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1412 break;
1413 case SkClipStack::Element::kRect_Type:
1414 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1415 break;
1416 case SkClipStack::Element::kEmpty_Type:
1417 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1418 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001419 }
1420 }
1421}
1422
reed@google.com5c3d1472011-02-22 19:12:23 +00001423///////////////////////////////////////////////////////////////////////////////
1424
reed@google.com3b3e8952012-08-16 20:53:31 +00001425bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001426
reed@google.com16078632011-12-06 18:56:37 +00001427 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001428 return true;
1429
reed@google.com00177082011-10-12 14:34:30 +00001430 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 return true;
1432 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433
tomhudson@google.com8d430182011-06-06 19:11:19 +00001434 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001435 SkRect dst;
1436 fMCRec->fMatrix->mapRect(&dst, rect);
1437 SkIRect idst;
1438 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001439 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001440 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001441 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001442
reed@android.coma380ae42009-07-21 01:17:02 +00001443 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001444 // TODO: should we use | instead, or compare all 4 at once?
1445 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001446 return true;
1447 }
reed@google.comc0784db2013-12-13 21:16:12 +00001448 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001449 return true;
1450 }
1451 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453}
1454
reed@google.com3b3e8952012-08-16 20:53:31 +00001455bool SkCanvas::quickReject(const SkPath& path) const {
1456 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457}
1458
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001459static inline int pinIntForScalar(int x) {
1460#ifdef SK_SCALAR_IS_FIXED
1461 if (x < SK_MinS16) {
1462 x = SK_MinS16;
1463 } else if (x > SK_MaxS16) {
1464 x = SK_MaxS16;
1465 }
1466#endif
1467 return x;
1468}
1469
reed@google.com3b3e8952012-08-16 20:53:31 +00001470bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001471 SkIRect ibounds;
1472 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 return false;
1474 }
1475
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001476 SkMatrix inverse;
1477 // if we can't invert the CTM, we can't return local clip bounds
1478 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001479 if (bounds) {
1480 bounds->setEmpty();
1481 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001482 return false;
1483 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001485 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001486 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001487 // adjust it outwards in case we are antialiasing
1488 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001489
1490 // SkRect::iset() will correctly assert if we pass a value out of range
1491 // (when SkScalar==fixed), so we pin to legal values. This does not
1492 // really returnt the correct answer, but its the best we can do given
1493 // that we've promised to return SkRect (even though we support devices
1494 // that can be larger than 32K in width or height).
1495 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1496 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001497 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001498 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 inverse.mapRect(bounds, r);
1500 }
1501 return true;
1502}
1503
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001504bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001505 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001506 if (clip.isEmpty()) {
1507 if (bounds) {
1508 bounds->setEmpty();
1509 }
1510 return false;
1511 }
1512
1513 if (NULL != bounds) {
1514 *bounds = clip.getBounds();
1515 }
1516 return true;
1517}
1518
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519const SkMatrix& SkCanvas::getTotalMatrix() const {
1520 return *fMCRec->fMatrix;
1521}
1522
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001523SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001524 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1525 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001526 return kComplex_ClipType;
1527}
1528
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001530 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531}
1532
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001533SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001534 int width, int height,
1535 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001536 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001537 if (device) {
1538 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1539 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001540 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001541 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001542 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543}
1544
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001545SkBaseDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001546 int width, int height,
1547 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001548 SkBaseDevice* device = this->getDevice();
bsalomon@google.come97f0852011-06-17 13:10:25 +00001549 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001550 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001551 } else {
1552 return NULL;
1553 }
1554}
1555
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001556GrContext* SkCanvas::getGrContext() {
1557#if SK_SUPPORT_GPU
1558 SkBaseDevice* device = this->getTopDevice();
1559 if (NULL != device) {
1560 GrRenderTarget* renderTarget = device->accessRenderTarget();
1561 if (NULL != renderTarget) {
1562 return renderTarget->getContext();
1563 }
1564 }
1565#endif
1566
1567 return NULL;
1568
1569}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001570
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571//////////////////////////////////////////////////////////////////////////////
1572// These are the virtual drawing methods
1573//////////////////////////////////////////////////////////////////////////////
1574
reed@google.com2a981812011-04-14 18:59:28 +00001575void SkCanvas::clear(SkColor color) {
1576 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001577 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001578 while (iter.next()) {
1579 iter.fDevice->clear(color);
1580 }
1581}
1582
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001584 this->internalDrawPaint(paint);
1585}
1586
1587void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001588 CHECK_SHADER_NOSETCONTEXT(paint);
1589
reed@google.com4e2b3d32011-04-07 14:18:59 +00001590 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591
1592 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001593 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 }
1595
reed@google.com4e2b3d32011-04-07 14:18:59 +00001596 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597}
1598
1599void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1600 const SkPaint& paint) {
1601 if ((long)count <= 0) {
1602 return;
1603 }
1604
reed@google.comea033602012-12-14 13:13:55 +00001605 CHECK_SHADER_NOSETCONTEXT(paint);
1606
reed@google.coma584aed2012-05-16 14:06:02 +00001607 if (paint.canComputeFastBounds()) {
1608 SkRect r;
1609 // special-case 2 points (common for drawing a single line)
1610 if (2 == count) {
1611 r.set(pts[0], pts[1]);
1612 } else {
1613 r.set(pts, count);
1614 }
1615 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001616 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001617 return;
1618 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001619 }
reed@google.coma584aed2012-05-16 14:06:02 +00001620
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 SkASSERT(pts != NULL);
1622
reed@google.com4e2b3d32011-04-07 14:18:59 +00001623 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001624
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001626 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627 }
reed@google.com4b226022011-01-11 18:32:13 +00001628
reed@google.com4e2b3d32011-04-07 14:18:59 +00001629 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630}
1631
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001632void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001633 CHECK_SHADER_NOSETCONTEXT(paint);
1634
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635 if (paint.canComputeFastBounds()) {
1636 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001637 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638 return;
1639 }
1640 }
reed@google.com4b226022011-01-11 18:32:13 +00001641
reed@google.com4e2b3d32011-04-07 14:18:59 +00001642 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643
1644 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001645 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646 }
1647
reed@google.com4e2b3d32011-04-07 14:18:59 +00001648 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649}
1650
reed@google.com4ed0fb72012-12-12 20:48:18 +00001651void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001652 CHECK_SHADER_NOSETCONTEXT(paint);
1653
reed@google.com4ed0fb72012-12-12 20:48:18 +00001654 if (paint.canComputeFastBounds()) {
1655 SkRect storage;
1656 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1657 return;
1658 }
1659 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001660
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001661 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1662
1663 while (iter.next()) {
1664 iter.fDevice->drawOval(iter, oval, looper.paint());
1665 }
1666
1667 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001668}
1669
1670void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001671 CHECK_SHADER_NOSETCONTEXT(paint);
1672
reed@google.com4ed0fb72012-12-12 20:48:18 +00001673 if (paint.canComputeFastBounds()) {
1674 SkRect storage;
1675 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1676 return;
1677 }
1678 }
1679
1680 if (rrect.isRect()) {
1681 // call the non-virtual version
1682 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001683 return;
1684 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001685 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001686 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1687 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001688 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001689
1690 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1691
1692 while (iter.next()) {
1693 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1694 }
1695
1696 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001697}
1698
1699
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001700void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001701 CHECK_SHADER_NOSETCONTEXT(paint);
1702
reed@google.com93645112012-07-26 16:11:47 +00001703 if (!path.isFinite()) {
1704 return;
1705 }
1706
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001707 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001708 SkRect storage;
1709 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001710 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 return;
1712 }
1713 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001714 if (path.isEmpty()) {
1715 if (path.isInverseFillType()) {
1716 this->internalDrawPaint(paint);
1717 }
1718 return;
1719 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720
reed@google.com4e2b3d32011-04-07 14:18:59 +00001721 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722
1723 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001724 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725 }
1726
reed@google.com4e2b3d32011-04-07 14:18:59 +00001727 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728}
1729
1730void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1731 const SkPaint* paint) {
1732 SkDEBUGCODE(bitmap.validate();)
1733
reed@google.com3d608122011-11-21 15:16:16 +00001734 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001735 SkRect bounds = {
1736 x, y,
1737 x + SkIntToScalar(bitmap.width()),
1738 y + SkIntToScalar(bitmap.height())
1739 };
1740 if (paint) {
1741 (void)paint->computeFastBounds(bounds, &bounds);
1742 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001743 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 return;
1745 }
1746 }
reed@google.com4b226022011-01-11 18:32:13 +00001747
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 SkMatrix matrix;
1749 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001750 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751}
1752
reed@google.com9987ec32011-09-07 11:57:52 +00001753// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001754void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001755 const SkRect& dst, const SkPaint* paint,
1756 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1758 return;
1759 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001760
reed@google.comea033602012-12-14 13:13:55 +00001761 CHECK_LOCKCOUNT_BALANCE(bitmap);
1762
reed@google.com3d608122011-11-21 15:16:16 +00001763 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001764 SkRect storage;
1765 const SkRect* bounds = &dst;
1766 if (paint) {
1767 bounds = &paint->computeFastBounds(dst, &storage);
1768 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001769 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001770 return;
1771 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 }
reed@google.com3d608122011-11-21 15:16:16 +00001773
reed@google.com33535f32012-09-25 15:37:50 +00001774 SkLazyPaint lazy;
1775 if (NULL == paint) {
1776 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001778
reed@google.com33535f32012-09-25 15:37:50 +00001779 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001780
reed@google.com33535f32012-09-25 15:37:50 +00001781 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001782 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001783 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001784
reed@google.com33535f32012-09-25 15:37:50 +00001785 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
reed@google.com71121732012-09-18 15:14:33 +00001788void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001789 const SkRect& dst, const SkPaint* paint,
1790 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001791 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001792 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001793}
1794
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1796 const SkPaint* paint) {
1797 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001798 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799}
1800
reed@google.com9987ec32011-09-07 11:57:52 +00001801void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1802 const SkIRect& center, const SkRect& dst,
1803 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001804 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001805 SkRect storage;
1806 const SkRect* bounds = &dst;
1807 if (paint) {
1808 bounds = &paint->computeFastBounds(dst, &storage);
1809 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001810 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001811 return;
1812 }
1813 }
1814
reed@google.com9987ec32011-09-07 11:57:52 +00001815 const int32_t w = bitmap.width();
1816 const int32_t h = bitmap.height();
1817
1818 SkIRect c = center;
1819 // pin center to the bounds of the bitmap
1820 c.fLeft = SkMax32(0, center.fLeft);
1821 c.fTop = SkMax32(0, center.fTop);
1822 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1823 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1824
reed@google.com71121732012-09-18 15:14:33 +00001825 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001826 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001827 };
1828 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001829 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001830 };
reed@google.com9987ec32011-09-07 11:57:52 +00001831 SkScalar dstX[4] = {
1832 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1833 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1834 };
1835 SkScalar dstY[4] = {
1836 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1837 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1838 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001839
reed@google.com9987ec32011-09-07 11:57:52 +00001840 if (dstX[1] > dstX[2]) {
1841 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1842 dstX[2] = dstX[1];
1843 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001844
reed@google.com9987ec32011-09-07 11:57:52 +00001845 if (dstY[1] > dstY[2]) {
1846 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1847 dstY[2] = dstY[1];
1848 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001849
reed@google.com9987ec32011-09-07 11:57:52 +00001850 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001851 SkRect s, d;
1852
reed@google.com9987ec32011-09-07 11:57:52 +00001853 s.fTop = srcY[y];
1854 s.fBottom = srcY[y+1];
1855 d.fTop = dstY[y];
1856 d.fBottom = dstY[y+1];
1857 for (int x = 0; x < 3; x++) {
1858 s.fLeft = srcX[x];
1859 s.fRight = srcX[x+1];
1860 d.fLeft = dstX[x];
1861 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001862 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001863 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001864 }
1865 }
1866}
1867
1868void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1869 const SkRect& dst, const SkPaint* paint) {
1870 SkDEBUGCODE(bitmap.validate();)
1871
1872 // Need a device entry-point, so gpu can use a mesh
1873 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1874}
1875
reed@google.comf67e4cf2011-03-15 20:56:58 +00001876class SkDeviceFilteredPaint {
1877public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001878 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1879 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001880 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001881 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001882 newPaint->setFlags(flags.fFlags);
1883 newPaint->setHinting(flags.fHinting);
1884 fPaint = newPaint;
1885 } else {
1886 fPaint = &paint;
1887 }
1888 }
1889
reed@google.comf67e4cf2011-03-15 20:56:58 +00001890 const SkPaint& paint() const { return *fPaint; }
1891
1892private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001893 const SkPaint* fPaint;
1894 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001895};
1896
bungeman@google.com52c748b2011-08-22 21:30:43 +00001897void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1898 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001899 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001900 draw.fDevice->drawRect(draw, r, paint);
1901 } else {
1902 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001903 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001904 draw.fDevice->drawRect(draw, r, p);
1905 }
1906}
1907
1908void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1909 const char text[], size_t byteLength,
1910 SkScalar x, SkScalar y) {
1911 SkASSERT(byteLength == 0 || text != NULL);
1912
1913 // nothing to draw
1914 if (text == NULL || byteLength == 0 ||
1915 draw.fClip->isEmpty() ||
1916 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1917 return;
1918 }
1919
1920 SkScalar width = 0;
1921 SkPoint start;
1922
1923 start.set(0, 0); // to avoid warning
1924 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1925 SkPaint::kStrikeThruText_Flag)) {
1926 width = paint.measureText(text, byteLength);
1927
1928 SkScalar offsetX = 0;
1929 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1930 offsetX = SkScalarHalf(width);
1931 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1932 offsetX = width;
1933 }
1934 start.set(x - offsetX, y);
1935 }
1936
1937 if (0 == width) {
1938 return;
1939 }
1940
1941 uint32_t flags = paint.getFlags();
1942
1943 if (flags & (SkPaint::kUnderlineText_Flag |
1944 SkPaint::kStrikeThruText_Flag)) {
1945 SkScalar textSize = paint.getTextSize();
1946 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1947 SkRect r;
1948
1949 r.fLeft = start.fX;
1950 r.fRight = start.fX + width;
1951
1952 if (flags & SkPaint::kUnderlineText_Flag) {
1953 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1954 start.fY);
1955 r.fTop = offset;
1956 r.fBottom = offset + height;
1957 DrawRect(draw, paint, r, textSize);
1958 }
1959 if (flags & SkPaint::kStrikeThruText_Flag) {
1960 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1961 start.fY);
1962 r.fTop = offset;
1963 r.fBottom = offset + height;
1964 DrawRect(draw, paint, r, textSize);
1965 }
1966 }
1967}
1968
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969void SkCanvas::drawText(const void* text, size_t byteLength,
1970 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001971 CHECK_SHADER_NOSETCONTEXT(paint);
1972
reed@google.com4e2b3d32011-04-07 14:18:59 +00001973 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974
1975 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001976 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001977 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001978 DrawTextDecorations(iter, dfp.paint(),
1979 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980 }
1981
reed@google.com4e2b3d32011-04-07 14:18:59 +00001982 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983}
1984
1985void SkCanvas::drawPosText(const void* text, size_t byteLength,
1986 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001987 CHECK_SHADER_NOSETCONTEXT(paint);
1988
reed@google.com4e2b3d32011-04-07 14:18:59 +00001989 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001990
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001992 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001993 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001994 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995 }
reed@google.com4b226022011-01-11 18:32:13 +00001996
reed@google.com4e2b3d32011-04-07 14:18:59 +00001997 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001998}
1999
2000void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2001 const SkScalar xpos[], SkScalar constY,
2002 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002003 CHECK_SHADER_NOSETCONTEXT(paint);
2004
reed@google.com4e2b3d32011-04-07 14:18:59 +00002005 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002006
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002008 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002010 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011 }
reed@google.com4b226022011-01-11 18:32:13 +00002012
reed@google.com4e2b3d32011-04-07 14:18:59 +00002013 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014}
2015
2016void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2017 const SkPath& path, const SkMatrix* matrix,
2018 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002019 CHECK_SHADER_NOSETCONTEXT(paint);
2020
reed@google.com4e2b3d32011-04-07 14:18:59 +00002021 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022
2023 while (iter.next()) {
2024 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002025 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 }
2027
reed@google.com4e2b3d32011-04-07 14:18:59 +00002028 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002029}
2030
2031void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2032 const SkPoint verts[], const SkPoint texs[],
2033 const SkColor colors[], SkXfermode* xmode,
2034 const uint16_t indices[], int indexCount,
2035 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002036 CHECK_SHADER_NOSETCONTEXT(paint);
2037
reed@google.com4e2b3d32011-04-07 14:18:59 +00002038 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002039
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 while (iter.next()) {
2041 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002042 colors, xmode, indices, indexCount,
2043 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 }
reed@google.com4b226022011-01-11 18:32:13 +00002045
reed@google.com4e2b3d32011-04-07 14:18:59 +00002046 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047}
2048
2049//////////////////////////////////////////////////////////////////////////////
2050// These methods are NOT virtual, and therefore must call back into virtual
2051// methods, rather than actually drawing themselves.
2052//////////////////////////////////////////////////////////////////////////////
2053
2054void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002055 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056 SkPaint paint;
2057
2058 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002059 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002060 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 }
2062 this->drawPaint(paint);
2063}
2064
reed@android.com845fdac2009-06-23 03:01:32 +00002065void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066 SkPaint paint;
2067
2068 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002069 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002070 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071 }
2072 this->drawPaint(paint);
2073}
2074
2075void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2076 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002077
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078 pt.set(x, y);
2079 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2080}
2081
2082void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2083 SkPoint pt;
2084 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002085
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 pt.set(x, y);
2087 paint.setColor(color);
2088 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2089}
2090
2091void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2092 const SkPaint& paint) {
2093 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002094
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 pts[0].set(x0, y0);
2096 pts[1].set(x1, y1);
2097 this->drawPoints(kLines_PointMode, 2, pts, paint);
2098}
2099
2100void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2101 SkScalar right, SkScalar bottom,
2102 const SkPaint& paint) {
2103 SkRect r;
2104
2105 r.set(left, top, right, bottom);
2106 this->drawRect(r, paint);
2107}
2108
2109void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2110 const SkPaint& paint) {
2111 if (radius < 0) {
2112 radius = 0;
2113 }
2114
2115 SkRect r;
2116 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002117 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118}
2119
2120void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2121 const SkPaint& paint) {
2122 if (rx > 0 && ry > 0) {
2123 if (paint.canComputeFastBounds()) {
2124 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002125 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002126 return;
2127 }
2128 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002129 SkRRect rrect;
2130 rrect.setRectXY(r, rx, ry);
2131 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132 } else {
2133 this->drawRect(r, paint);
2134 }
2135}
2136
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2138 SkScalar sweepAngle, bool useCenter,
2139 const SkPaint& paint) {
2140 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2141 this->drawOval(oval, paint);
2142 } else {
2143 SkPath path;
2144 if (useCenter) {
2145 path.moveTo(oval.centerX(), oval.centerY());
2146 }
2147 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2148 if (useCenter) {
2149 path.close();
2150 }
2151 this->drawPath(path, paint);
2152 }
2153}
2154
2155void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2156 const SkPath& path, SkScalar hOffset,
2157 SkScalar vOffset, const SkPaint& paint) {
2158 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002159
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 matrix.setTranslate(hOffset, vOffset);
2161 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2162}
2163
reed@android.comf76bacf2009-05-13 14:00:33 +00002164///////////////////////////////////////////////////////////////////////////////
2165
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002168}
2169
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170///////////////////////////////////////////////////////////////////////////////
2171///////////////////////////////////////////////////////////////////////////////
2172
2173SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002174 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175
2176 SkASSERT(canvas);
2177
2178 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2179 fDone = !fImpl->next();
2180}
2181
2182SkCanvas::LayerIter::~LayerIter() {
2183 fImpl->~SkDrawIter();
2184}
2185
2186void SkCanvas::LayerIter::next() {
2187 fDone = !fImpl->next();
2188}
2189
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002190SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 return fImpl->getDevice();
2192}
2193
2194const SkMatrix& SkCanvas::LayerIter::matrix() const {
2195 return fImpl->getMatrix();
2196}
2197
2198const SkPaint& SkCanvas::LayerIter::paint() const {
2199 const SkPaint* paint = fImpl->getPaint();
2200 if (NULL == paint) {
2201 paint = &fDefaultPaint;
2202 }
2203 return *paint;
2204}
2205
2206const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2207int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2208int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002209
2210///////////////////////////////////////////////////////////////////////////////
2211
2212SkCanvas::ClipVisitor::~ClipVisitor() { }