blob: 47d3cca81edc8ee2c5acf3f469187e3fec51fdb1 [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@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
94class AutoCheckNoSetContext {
95public:
96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
97 this->assertNoSetContext(fPaint);
98 }
99 ~AutoCheckNoSetContext() {
100 this->assertNoSetContext(fPaint);
101 }
102
103private:
104 const SkPaint& fPaint;
105
106 void assertNoSetContext(const SkPaint& paint) {
107 SkShader* s = paint.getShader();
108 if (s) {
109 SkASSERT(!s->setContextHasBeenCalled());
110 }
111 }
112};
113
114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
119 #define CHECK_SHADER_NOSETCONTEXT(paint)
120#endif
121
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000122typedef SkTLazy<SkPaint> SkLazyPaint;
123
reed@google.com97af1a62012-08-28 12:19:02 +0000124void SkCanvas::predrawNotify() {
125 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000127 }
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000132/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 The clip/matrix/proc are fields that reflect the top of the save/restore
134 stack. Whenever the canvas changes, it marks a dirty flag, and then before
135 these are used (assuming we're not on a layer) we rebuild these cache
136 values: they reflect the top of the save stack, but translated and clipped
137 by the device's XY offset and bitmap-bounds.
138*/
139struct DeviceCM {
140 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000141 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000142 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000146 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 : fNext(NULL) {
148 if (NULL != device) {
149 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000150 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@google.com4b226022011-01-11 18:32:13 +0000152 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000156 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000158 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fDevice->unref();
160 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000161 SkDELETE(fPaint);
162 }
reed@google.com4b226022011-01-11 18:32:13 +0000163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
165 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000166 int x = fDevice->getOrigin().x();
167 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 int width = fDevice->width();
169 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if ((x | y) == 0) {
172 fMatrix = &totalMatrix;
173 fClip = totalClip;
174 } else {
175 fMatrixStorage = totalMatrix;
176 fMatrixStorage.postTranslate(SkIntToScalar(-x),
177 SkIntToScalar(-y));
178 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 totalClip.translate(-x, -y, &fClip);
181 }
182
reed@google.com045e62d2011-10-24 12:19:46 +0000183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000186
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkRegion::kDifference_Op);
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#ifdef SK_DEBUG
195 if (!fClip.isEmpty()) {
196 SkIRect deviceR;
197 deviceR.set(0, 0, width, height);
198 SkASSERT(deviceR.contains(fClip.getBounds()));
199 }
200#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000201 }
202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000204 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207/* This is the record we keep for each save/restore level in the stack.
208 Since a level optionally copies the matrix and/or stack, we have pointers
209 for these fields. If the value is copied for this level, the copy is
210 stored in the ...Storage field, and the pointer points to that. If the
211 value is not copied for this level, we ignore ...Storage, and just point
212 at the corresponding value in the previous level in the stack.
213*/
214class SkCanvas::MCRec {
215public:
216 MCRec* fNext;
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000217 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000218 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
219 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
220 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 DeviceCM* fLayer;
223 /* If there are any layers in the stack, this points to the top-most
224 one that is at or below this level in the stack (so we know what
225 bitmap/device to draw into from this level. This value is NOT
226 reference counted, since the real owner is either our fLayer field,
227 or a previous one in a lower level.)
228 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000229 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000231 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 if (NULL != prev) {
233 if (flags & SkCanvas::kMatrix_SaveFlag) {
234 fMatrixStorage = *prev->fMatrix;
235 fMatrix = &fMatrixStorage;
236 } else {
237 fMatrix = prev->fMatrix;
238 }
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000241 fRasterClipStorage = *prev->fRasterClip;
242 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 } else {
reed@google.com00177082011-10-12 14:34:30 +0000244 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 }
246
247 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000248 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249
250 fTopLayer = prev->fTopLayer;
251 } else { // no prev
252 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000255 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 fFilter = NULL;
257 fTopLayer = NULL;
258 }
259 fLayer = NULL;
260
261 // don't bother initializing fNext
262 inc_rec();
263 }
264 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000265 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkDELETE(fLayer);
267 dec_rec();
268 }
reed@google.com4b226022011-01-11 18:32:13 +0000269
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270private:
reed@google.com00177082011-10-12 14:34:30 +0000271 SkMatrix fMatrixStorage;
272 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
275class SkDrawIter : public SkDraw {
276public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000277 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000278 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000279 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 canvas->updateDeviceCMCache();
281
reed@google.com90c07ea2012-04-13 13:50:27 +0000282 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fBounder = canvas->getBounder();
284 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000285 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 }
reed@google.com4b226022011-01-11 18:32:13 +0000287
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 bool next() {
289 // skip over recs with empty clips
290 if (fSkipEmptyClips) {
291 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
292 fCurrLayer = fCurrLayer->fNext;
293 }
294 }
295
reed@google.comf68c5e22012-02-24 16:38:58 +0000296 const DeviceCM* rec = fCurrLayer;
297 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298
299 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000300 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
301 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 fDevice = rec->fDevice;
303 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000305 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306
307 fCurrLayer = rec->fNext;
308 if (fBounder) {
309 fBounder->setClip(fClip);
310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000312
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 return true;
314 }
315 return false;
316 }
reed@google.com4b226022011-01-11 18:32:13 +0000317
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000318 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000319 int getX() const { return fDevice->getOrigin().x(); }
320 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 const SkMatrix& getMatrix() const { return *fMatrix; }
322 const SkRegion& getClip() const { return *fClip; }
323 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000324
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325private:
326 SkCanvas* fCanvas;
327 const DeviceCM* fCurrLayer;
328 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 SkBool8 fSkipEmptyClips;
330
331 typedef SkDraw INHERITED;
332};
333
334/////////////////////////////////////////////////////////////////////////////
335
336class AutoDrawLooper {
337public:
reed@google.com8926b162012-03-23 15:36:36 +0000338 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
339 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fCanvas = canvas;
341 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000343 fPaint = NULL;
344 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000345 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000346 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
reed@google.com8926b162012-03-23 15:36:36 +0000348 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
349 SkPaint tmp;
350 tmp.setImageFilter(fOrigPaint.getImageFilter());
351 // it would be nice if we had a guess at the bounds, instead of null
352 (void)canvas->internalSaveLayer(NULL, &tmp,
353 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
354 // we'll clear the imageFilter for the actual draws in next(), so
355 // it will only be applied during the restore().
356 fDoClearImageFilter = true;
357 }
358
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359 if (fLooper) {
360 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000361 fIsSimple = false;
362 } else {
363 // can we be marked as simple?
364 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000365 }
366 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000367
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000369 if (fDoClearImageFilter) {
370 fCanvas->internalRestore();
371 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000372 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000374
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 const SkPaint& paint() const {
376 SkASSERT(fPaint);
377 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000379
reed@google.com129ec222012-05-15 13:24:09 +0000380 bool next(SkDrawFilter::Type drawType) {
381 if (fDone) {
382 return false;
383 } else if (fIsSimple) {
384 fDone = true;
385 fPaint = &fOrigPaint;
386 return !fPaint->nothingToDraw();
387 } else {
388 return this->doNext(drawType);
389 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000390 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000391
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000393 SkLazyPaint fLazyPaint;
394 SkCanvas* fCanvas;
395 const SkPaint& fOrigPaint;
396 SkDrawLooper* fLooper;
397 SkDrawFilter* fFilter;
398 const SkPaint* fPaint;
399 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000400 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000401 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000402 bool fIsSimple;
403
404 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405};
406
reed@google.com129ec222012-05-15 13:24:09 +0000407bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000408 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000409 SkASSERT(!fIsSimple);
410 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
411
412 SkPaint* paint = fLazyPaint.set(fOrigPaint);
413
414 if (fDoClearImageFilter) {
415 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000417
reed@google.com129ec222012-05-15 13:24:09 +0000418 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000420 return false;
421 }
422 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000423 if (!fFilter->filter(paint, drawType)) {
424 fDone = true;
425 return false;
426 }
reed@google.com129ec222012-05-15 13:24:09 +0000427 if (NULL == fLooper) {
428 // no looper means we only draw once
429 fDone = true;
430 }
431 }
432 fPaint = paint;
433
434 // if we only came in here for the imagefilter, mark us as done
435 if (!fLooper && !fFilter) {
436 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000437 }
438
439 // call this after any possible paint modifiers
440 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000441 fPaint = NULL;
442 return false;
443 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000444 return true;
445}
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447/* Stack helper for managing a SkBounder. In the destructor, if we were
448 given a bounder, we call its commit() method, signifying that we are
449 done accumulating bounds for that draw.
450*/
451class SkAutoBounderCommit {
452public:
453 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
454 ~SkAutoBounderCommit() {
455 if (NULL != fBounder) {
456 fBounder->commit();
457 }
458 }
459private:
460 SkBounder* fBounder;
461};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000462#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463
464#include "SkColorPriv.h"
465
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466////////// macros to place around the internal draw calls //////////////////
467
reed@google.com8926b162012-03-23 15:36:36 +0000468#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000469 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000470 AutoDrawLooper looper(this, paint, true); \
471 while (looper.next(type)) { \
472 SkAutoBounderCommit ac(fBounder); \
473 SkDrawIter iter(this);
474
reed@google.com4e2b3d32011-04-07 14:18:59 +0000475#define LOOPER_BEGIN(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000476 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000477 AutoDrawLooper looper(this, paint); \
478 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 SkAutoBounderCommit ac(fBounder); \
480 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000481
reed@google.com4e2b3d32011-04-07 14:18:59 +0000482#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483
484////////////////////////////////////////////////////////////////////////////
485
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000486SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000488 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000490 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000491 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000492 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000493 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000494 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495
496 fMCRec = (MCRec*)fMCStack.push_back();
497 new (fMCRec) MCRec(NULL, 0);
498
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000499 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 fMCRec->fTopLayer = fMCRec->fLayer;
501 fMCRec->fNext = NULL;
502
reed@google.com97af1a62012-08-28 12:19:02 +0000503 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 return this->setDevice(device);
506}
507
reed@google.comcde92112011-07-06 20:00:52 +0000508SkCanvas::SkCanvas()
509: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000510 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000511
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000512 this->init(NULL);
513}
514
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000515SkCanvas::SkCanvas(SkBaseDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000516 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 inc_canvas();
518
519 this->init(device);
520}
521
522SkCanvas::SkCanvas(const SkBitmap& bitmap)
523 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
524 inc_canvas();
525
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000526 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527}
528
529SkCanvas::~SkCanvas() {
530 // free up the contents of our deque
531 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000532 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 this->internalRestore(); // restore the last, since we're going away
535
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000536 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000537 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000538
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 dec_canvas();
540}
541
542SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
543 SkRefCnt_SafeAssign(fBounder, bounder);
544 return bounder;
545}
546
547SkDrawFilter* SkCanvas::getDrawFilter() const {
548 return fMCRec->fFilter;
549}
550
551SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
552 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
553 return filter;
554}
555
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000556SkMetaData& SkCanvas::getMetaData() {
557 // metadata users are rare, so we lazily allocate it. If that changes we
558 // can decide to just make it a field in the device (rather than a ptr)
559 if (NULL == fMetaData) {
560 fMetaData = new SkMetaData;
561 }
562 return *fMetaData;
563}
564
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565///////////////////////////////////////////////////////////////////////////////
566
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000567void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000568 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000569 if (device) {
570 device->flush();
571 }
572}
573
reed@google.com210ce002011-11-01 14:24:23 +0000574SkISize SkCanvas::getDeviceSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000575 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000576 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
577}
578
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000579SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000581 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 SkASSERT(rec && rec->fLayer);
583 return rec->fLayer->fDevice;
584}
585
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000586SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000587 if (updateMatrixClip) {
588 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
589 }
reed@google.com9266fed2011-03-30 00:18:03 +0000590 return fMCRec->fTopLayer->fDevice;
591}
592
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000593SkBaseDevice* SkCanvas::setDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000595 SkDeque::F2BIter iter(fMCStack);
596 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000598 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599
600 if (rootDevice == device) {
601 return device;
602 }
reed@google.com4b226022011-01-11 18:32:13 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000605 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 }
607 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000608 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 }
610
611 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
612 rootDevice = device;
613
614 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 /* Now we update our initial region to have the bounds of the new device,
617 and then intersect all of the clips in our stack with these bounds,
618 to ensure that we can't draw outside of the device's bounds (and trash
619 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 NOTE: this is only a partial-fix, since if the new device is larger than
622 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000623 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
625 reconstruct the correct clips, so this approximation will have to do.
626 The caller really needs to restore() back to the base if they want to
627 accurately take advantage of the new device bounds.
628 */
629
reed@google.com42aea282012-03-28 16:19:15 +0000630 SkIRect bounds;
631 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000633 } else {
634 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 }
reed@google.com42aea282012-03-28 16:19:15 +0000636 // now jam our 1st clip to be bounds, and intersect the rest with that
637 rec->fRasterClip->setRect(bounds);
638 while ((rec = (MCRec*)iter.next()) != NULL) {
639 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
640 }
641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 return device;
643}
644
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000645bool SkCanvas::readPixels(SkBitmap* bitmap,
646 int x, int y,
647 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000648 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000649 if (!device) {
650 return false;
651 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000652 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000653}
654
bsalomon@google.comc6980972011-11-02 19:57:21 +0000655bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000656 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000657 if (!device) {
658 return false;
659 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000660
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000661 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000662 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000663 if (!bounds.intersect(srcRect)) {
664 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000665 }
666
667 SkBitmap tmp;
668 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
669 bounds.height());
670 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
671 bitmap->swap(tmp);
672 return true;
673 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000674 return false;
675 }
reed@google.com51df9e32010-12-23 19:29:18 +0000676}
677
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000678void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
679 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000680 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000681 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000682 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
683 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
684 device->accessBitmap(true);
685 device->writePixels(bitmap, x, y, config8888);
686 }
reed@google.com51df9e32010-12-23 19:29:18 +0000687 }
688}
689
junov@google.com4370aed2012-01-18 16:21:08 +0000690SkCanvas* SkCanvas::canvasForDrawIter() {
691 return this;
692}
693
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694//////////////////////////////////////////////////////////////////////////////
695
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696void SkCanvas::updateDeviceCMCache() {
697 if (fDeviceCMDirty) {
698 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000699 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000701
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000703 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000705 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000707 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 } while ((layer = layer->fNext) != NULL);
709 }
710 fDeviceCMDirty = false;
711 }
712}
713
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714///////////////////////////////////////////////////////////////////////////////
715
716int SkCanvas::internalSave(SaveFlags flags) {
717 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 MCRec* newTop = (MCRec*)fMCStack.push_back();
720 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 newTop->fNext = fMCRec;
723 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000724
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000725 if (SkCanvas::kClip_SaveFlag & flags) {
726 fClipStack.save();
727 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000728
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 return saveCount;
730}
731
732int SkCanvas::save(SaveFlags flags) {
733 // call shared impl
734 return this->internalSave(flags);
735}
736
737#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
738#define C16MASK (1 << SkBitmap::kRGB_565_Config)
739#define C8MASK (1 << SkBitmap::kA8_Config)
740
741static SkBitmap::Config resolve_config(SkCanvas* canvas,
742 const SkIRect& bounds,
743 SkCanvas::SaveFlags flags,
744 bool* isOpaque) {
745 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
746
747#if 0
748 // loop through and union all the configs we may draw into
749 uint32_t configMask = 0;
750 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
751 {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000752 SkBaseDevice* device = canvas->getLayerDevice(i);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 if (device->intersects(bounds))
754 configMask |= 1 << device->config();
755 }
756
757 // if the caller wants alpha or fullcolor, we can't return 565
758 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
759 SkCanvas::kHasAlphaLayer_SaveFlag))
760 configMask &= ~C16MASK;
761
762 switch (configMask) {
763 case C8MASK: // if we only have A8, return that
764 return SkBitmap::kA8_Config;
765
766 case C16MASK: // if we only have 565, return that
767 return SkBitmap::kRGB_565_Config;
768
769 default:
770 return SkBitmap::kARGB_8888_Config; // default answer
771 }
772#else
773 return SkBitmap::kARGB_8888_Config; // default answer
774#endif
775}
776
777static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
778 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
779}
780
junov@chromium.orga907ac32012-02-24 21:54:07 +0000781bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
782 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000783 SkIRect clipBounds;
784 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000785 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000786 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000787 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 if (NULL != bounds) {
789 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 this->getTotalMatrix().mapRect(&r, *bounds);
792 r.roundOut(&ir);
793 // early exit if the layer's bounds are clipped out
794 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000795 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000796 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000797 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000798 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 }
800 } else { // no user bounds, so just use the clip
801 ir = clipBounds;
802 }
803
reed@google.com5c3d1472011-02-22 19:12:23 +0000804 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000805
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 // early exit if the clip is now empty
807 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000808 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000809 return false;
810 }
811
812 if (intersection) {
813 *intersection = ir;
814 }
815 return true;
816}
817
818int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
819 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000820 return this->internalSaveLayer(bounds, paint, flags, false);
821}
822
823int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
824 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000825 // do this before we create the layer. We don't call the public save() since
826 // that would invoke a possibly overridden virtual
827 int count = this->internalSave(flags);
828
829 fDeviceCMDirty = true;
830
831 SkIRect ir;
832 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 return count;
834 }
835
reed@google.comb55deeb2012-01-06 14:43:09 +0000836 // Kill the imagefilter if our device doesn't allow it
837 SkLazyPaint lazyP;
838 if (paint && paint->getImageFilter()) {
839 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000840 if (justForImageFilter) {
841 // early exit if the layer was just for the imageFilter
842 return count;
843 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000844 SkPaint* p = lazyP.set(*paint);
845 p->setImageFilter(NULL);
846 paint = p;
847 }
848 }
849
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 bool isOpaque;
851 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
852
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000853 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000854 if (paint && paint->getImageFilter()) {
855 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
856 isOpaque);
857 } else {
858 device = this->createLayerDevice(config, ir.width(), ir.height(),
859 isOpaque);
860 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000861 if (NULL == device) {
862 SkDebugf("Unable to create device for layer.");
863 return count;
864 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000865
reed@google.com6f8f2922011-03-04 22:27:10 +0000866 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000867 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 device->unref();
869
870 layer->fNext = fMCRec->fTopLayer;
871 fMCRec->fLayer = layer;
872 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
873
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000874 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 return count;
876}
877
878int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
879 SaveFlags flags) {
880 if (0xFF == alpha) {
881 return this->saveLayer(bounds, NULL, flags);
882 } else {
883 SkPaint tmpPaint;
884 tmpPaint.setAlpha(alpha);
885 return this->saveLayer(bounds, &tmpPaint, flags);
886 }
887}
888
889void SkCanvas::restore() {
890 // check for underflow
891 if (fMCStack.count() > 1) {
892 this->internalRestore();
893 }
894}
895
896void SkCanvas::internalRestore() {
897 SkASSERT(fMCStack.count() != 0);
898
899 fDeviceCMDirty = true;
900 fLocalBoundsCompareTypeDirty = true;
901
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000902 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
903 fClipStack.restore();
904 }
905
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000906 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 DeviceCM* layer = fMCRec->fLayer; // may be null
908 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
909 fMCRec->fLayer = NULL;
910
911 // now do the normal restore()
912 fMCRec->~MCRec(); // balanced in save()
913 fMCStack.pop_back();
914 fMCRec = (MCRec*)fMCStack.back();
915
916 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
917 since if we're being recorded, we don't want to record this (the
918 recorder will have already recorded the restore).
919 */
920 if (NULL != layer) {
921 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000922 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000923 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
924 layer->fPaint);
925 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000927
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000928 SkASSERT(fSaveLayerCount > 0);
929 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 }
931 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000932 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933}
934
935int SkCanvas::getSaveCount() const {
936 return fMCStack.count();
937}
938
939void SkCanvas::restoreToCount(int count) {
940 // sanity check
941 if (count < 1) {
942 count = 1;
943 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000944
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000945 int n = this->getSaveCount() - count;
946 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 this->restore();
948 }
949}
950
reed@google.com7c202932011-12-14 18:48:05 +0000951bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000952 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000953}
954
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955/////////////////////////////////////////////////////////////////////////////
956
957// can't draw it if its empty, or its too big for a fixed-point width or height
958static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +0000959 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960}
961
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000962void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 const SkMatrix& matrix, const SkPaint* paint) {
964 if (reject_bitmap(bitmap)) {
965 return;
966 }
967
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000968 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000970 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000972
973 SkDEBUGCODE(bitmap.validate();)
974 CHECK_LOCKCOUNT_BALANCE(bitmap);
975
976 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
977
978 while (iter.next()) {
979 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
980 }
981
982 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983}
984
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000985void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +0000986 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 SkPaint tmp;
988 if (NULL == paint) {
989 tmp.setDither(true);
990 paint = &tmp;
991 }
reed@google.com4b226022011-01-11 18:32:13 +0000992
reed@google.com8926b162012-03-23 15:36:36 +0000993 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000995 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000996 paint = &looper.paint();
997 SkImageFilter* filter = paint->getImageFilter();
998 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000999 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001000 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001001 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +00001002 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001003 SkMatrix matrix = *iter.fMatrix;
1004 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1005 if (filter->filterImage(&proxy, src, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001006 SkPaint tmpUnfiltered(*paint);
1007 tmpUnfiltered.setImageFilter(NULL);
1008 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001009 }
1010 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001011 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001012 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001014 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015}
1016
reed@google.com8926b162012-03-23 15:36:36 +00001017void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1018 const SkPaint* paint) {
1019 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001020 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001021
reed@google.com8926b162012-03-23 15:36:36 +00001022 if (reject_bitmap(bitmap)) {
1023 return;
1024 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001025
reed@google.com8926b162012-03-23 15:36:36 +00001026 SkPaint tmp;
1027 if (NULL == paint) {
1028 paint = &tmp;
1029 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001030
reed@google.com8926b162012-03-23 15:36:36 +00001031 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001032
reed@google.com8926b162012-03-23 15:36:36 +00001033 while (iter.next()) {
1034 paint = &looper.paint();
1035 SkImageFilter* filter = paint->getImageFilter();
1036 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1037 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001038 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001039 SkBitmap dst;
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001040 SkMatrix matrix = *iter.fMatrix;
1041 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1042 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001043 SkPaint tmpUnfiltered(*paint);
1044 tmpUnfiltered.setImageFilter(NULL);
1045 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1046 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001047 }
1048 } else {
1049 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1050 }
1051 }
1052 LOOPER_END
1053}
1054
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055/////////////////////////////////////////////////////////////////////////////
1056
1057bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1058 fDeviceCMDirty = true;
1059 fLocalBoundsCompareTypeDirty = true;
1060 return fMCRec->fMatrix->preTranslate(dx, dy);
1061}
1062
1063bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1064 fDeviceCMDirty = true;
1065 fLocalBoundsCompareTypeDirty = true;
1066 return fMCRec->fMatrix->preScale(sx, sy);
1067}
1068
1069bool SkCanvas::rotate(SkScalar degrees) {
1070 fDeviceCMDirty = true;
1071 fLocalBoundsCompareTypeDirty = true;
1072 return fMCRec->fMatrix->preRotate(degrees);
1073}
1074
1075bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1076 fDeviceCMDirty = true;
1077 fLocalBoundsCompareTypeDirty = true;
1078 return fMCRec->fMatrix->preSkew(sx, sy);
1079}
1080
1081bool SkCanvas::concat(const SkMatrix& matrix) {
1082 fDeviceCMDirty = true;
1083 fLocalBoundsCompareTypeDirty = true;
1084 return fMCRec->fMatrix->preConcat(matrix);
1085}
1086
1087void SkCanvas::setMatrix(const SkMatrix& matrix) {
1088 fDeviceCMDirty = true;
1089 fLocalBoundsCompareTypeDirty = true;
1090 *fMCRec->fMatrix = matrix;
1091}
1092
1093// this is not virtual, so it must call a virtual method so that subclasses
1094// will see its action
1095void SkCanvas::resetMatrix() {
1096 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001097
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 matrix.reset();
1099 this->setMatrix(matrix);
1100}
1101
1102//////////////////////////////////////////////////////////////////////////////
1103
reed@google.comc42d35d2011-10-12 11:57:42 +00001104bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001105#ifdef SK_ENABLE_CLIP_QUICKREJECT
1106 if (SkRegion::kIntersect_Op == op) {
1107 if (fMCRec->fRasterClip->isEmpty()) {
1108 return false;
1109 }
1110
reed@google.com3b3e8952012-08-16 20:53:31 +00001111 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001112 fDeviceCMDirty = true;
1113 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001114
1115 fClipStack.clipEmpty();
1116 return fMCRec->fRasterClip->setEmpty();
1117 }
1118 }
1119#endif
1120
reed@google.com5c3d1472011-02-22 19:12:23 +00001121 AutoValidateClip avc(this);
1122
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 fDeviceCMDirty = true;
1124 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001125 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126
1127 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001128 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001129 // the matrix. This means we don't have to a) make a path, and b) tell
1130 // the region code to scan-convert the path, only to discover that it
1131 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133
1134 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001135 fClipStack.clipDevRect(r, op, doAA);
1136 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001138 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001139 // and clip against that, since it can handle any matrix. However, to
1140 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1141 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 SkPath path;
1143
1144 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001145 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 }
1147}
1148
reed@google.com00177082011-10-12 14:34:30 +00001149static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001150 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001151 // base is used to limit the size (and therefore memory allocation) of the
1152 // region that results from scan converting devPath.
1153 SkRegion base;
1154
reed@google.com819c9212011-02-23 18:56:55 +00001155 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001156 // since we are intersect, we can do better (tighter) with currRgn's
1157 // bounds, than just using the device. However, if currRgn is complex,
1158 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001159 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001160 // FIXME: we should also be able to do this when currClip->isBW(),
1161 // but relaxing the test above triggers GM asserts in
1162 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1163 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001164 } else {
reed@google.com00177082011-10-12 14:34:30 +00001165 base.setRect(currClip->getBounds());
1166 SkRasterClip clip;
1167 clip.setPath(devPath, base, doAA);
1168 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001169 }
reed@google.com819c9212011-02-23 18:56:55 +00001170 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001171 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001172 if (!device) {
1173 return currClip->setEmpty();
1174 }
1175
junov@chromium.orga907ac32012-02-24 21:54:07 +00001176 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001177
1178 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001179 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001180 } else {
reed@google.com00177082011-10-12 14:34:30 +00001181 SkRasterClip clip;
1182 clip.setPath(devPath, base, doAA);
1183 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001184 }
1185 }
1186}
1187
reed@google.com4ed0fb72012-12-12 20:48:18 +00001188bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1189 if (rrect.isRect()) {
1190 // call the non-virtual version
1191 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1192 } else {
1193 SkPath path;
1194 path.addRRect(rrect);
1195 // call the non-virtual version
1196 return this->SkCanvas::clipPath(path, op, doAA);
1197 }
1198}
1199
reed@google.comc42d35d2011-10-12 11:57:42 +00001200bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001201#ifdef SK_ENABLE_CLIP_QUICKREJECT
1202 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1203 if (fMCRec->fRasterClip->isEmpty()) {
1204 return false;
1205 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001206
reed@google.com3b3e8952012-08-16 20:53:31 +00001207 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001208 fDeviceCMDirty = true;
1209 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001210
reed@google.comda17f752012-08-16 18:27:05 +00001211 fClipStack.clipEmpty();
1212 return fMCRec->fRasterClip->setEmpty();
1213 }
1214 }
1215#endif
1216
reed@google.com5c3d1472011-02-22 19:12:23 +00001217 AutoValidateClip avc(this);
1218
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 fDeviceCMDirty = true;
1220 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001221 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222
1223 SkPath devPath;
1224 path.transform(*fMCRec->fMatrix, &devPath);
1225
reed@google.comfe701122011-11-08 19:41:23 +00001226 // Check if the transfomation, or the original path itself
1227 // made us empty. Note this can also happen if we contained NaN
1228 // values. computing the bounds detects this, and will set our
1229 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1230 if (devPath.getBounds().isEmpty()) {
1231 // resetting the path will remove any NaN or other wanky values
1232 // that might upset our scan converter.
1233 devPath.reset();
1234 }
1235
reed@google.com5c3d1472011-02-22 19:12:23 +00001236 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001237 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001238
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001239 if (fAllowSimplifyClip) {
1240 devPath.reset();
1241 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1242 const SkClipStack* clipStack = getClipStack();
1243 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1244 const SkClipStack::Element* element;
1245 while ((element = iter.next())) {
1246 SkClipStack::Element::Type type = element->getType();
1247 if (type == SkClipStack::Element::kEmpty_Type) {
1248 continue;
1249 }
1250 SkPath operand;
1251 if (type == SkClipStack::Element::kRect_Type) {
1252 operand.addRect(element->getRect());
1253 } else if (type == SkClipStack::Element::kPath_Type) {
1254 operand = element->getPath();
1255 } else {
1256 SkDEBUGFAIL("Unexpected type.");
1257 }
1258 SkRegion::Op elementOp = element->getOp();
1259 if (elementOp == SkRegion::kReplace_Op) {
1260 devPath = operand;
1261 } else {
1262 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1263 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001264 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1265 // perhaps we need an API change to avoid this sort of mixed-signals about
1266 // clipping.
1267 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001268 }
1269 op = SkRegion::kReplace_Op;
1270 }
1271
reed@google.com00177082011-10-12 14:34:30 +00001272 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273}
1274
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001275bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1276 bool inverseFilled) {
1277 // This is for updating the clip conservatively using only bounds
1278 // information.
1279 // Contract:
1280 // The current clip must contain the true clip. The true
1281 // clip is the clip that would have normally been computed
1282 // by calls to clipPath and clipRRect
1283 // Objective:
1284 // Keep the current clip as small as possible without
1285 // breaking the contract, using only clip bounding rectangles
1286 // (for performance).
1287
1288 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1289 // don't have to worry about getting caught in a loop. Thus anywhere
1290 // we call a virtual method, we explicitly prefix it with
1291 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001292
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001293 if (inverseFilled) {
1294 switch (op) {
1295 case SkRegion::kIntersect_Op:
1296 case SkRegion::kDifference_Op:
1297 // These ops can only shrink the current clip. So leaving
1298 // the clip unchanges conservatively respects the contract.
1299 return this->getClipDeviceBounds(NULL);
1300 case SkRegion::kUnion_Op:
1301 case SkRegion::kReplace_Op:
1302 case SkRegion::kReverseDifference_Op:
1303 case SkRegion::kXOR_Op:
1304 {
1305 // These ops can grow the current clip up to the extents of
1306 // the input clip, which is inverse filled, so we just set
1307 // the current clip to the device bounds.
1308 SkRect deviceBounds;
1309 SkIRect deviceIBounds;
1310 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001311 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001312 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1313 // set the clip in device space
1314 this->SkCanvas::setMatrix(SkMatrix::I());
1315 bool result = this->SkCanvas::clipRect(deviceBounds,
1316 SkRegion::kReplace_Op, false);
1317 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1318 return result;
1319 }
1320 default:
1321 SkASSERT(0); // unhandled op?
1322 }
1323 } else {
1324 // Not inverse filled
1325 switch (op) {
1326 case SkRegion::kIntersect_Op:
1327 case SkRegion::kUnion_Op:
1328 case SkRegion::kReplace_Op:
1329 return this->SkCanvas::clipRect(bounds, op, false);
1330 case SkRegion::kDifference_Op:
1331 // Difference can only shrink the current clip.
1332 // Leaving clip unchanged conservatively fullfills the contract.
1333 return this->getClipDeviceBounds(NULL);
1334 case SkRegion::kReverseDifference_Op:
1335 // To reverse, we swap in the bounds with a replace op.
1336 // As with difference, leave it unchanged.
1337 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1338 case SkRegion::kXOR_Op:
1339 // Be conservative, based on (A XOR B) always included in (A union B),
1340 // which is always included in (bounds(A) union bounds(B))
1341 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1342 default:
1343 SkASSERT(0); // unhandled op?
1344 }
1345 }
1346 return true;
1347}
1348
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001350 AutoValidateClip avc(this);
1351
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 fDeviceCMDirty = true;
1353 fLocalBoundsCompareTypeDirty = true;
1354
reed@google.com5c3d1472011-02-22 19:12:23 +00001355 // todo: signal fClipStack that we have a region, and therefore (I guess)
1356 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001357 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001358
reed@google.com00177082011-10-12 14:34:30 +00001359 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360}
1361
reed@google.com819c9212011-02-23 18:56:55 +00001362#ifdef SK_DEBUG
1363void SkCanvas::validateClip() const {
1364 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001365 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001366 if (!device) {
1367 SkASSERT(this->getTotalClip().isEmpty());
1368 return;
1369 }
1370
reed@google.com819c9212011-02-23 18:56:55 +00001371 SkIRect ir;
1372 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001373 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001374
robertphillips@google.com80214e22012-07-20 15:33:18 +00001375 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001376 const SkClipStack::Element* element;
1377 while ((element = iter.next()) != NULL) {
1378 switch (element->getType()) {
1379 case SkClipStack::Element::kPath_Type:
1380 clipPathHelper(this,
1381 &tmpClip,
1382 element->getPath(),
1383 element->getOp(),
1384 element->isAA());
1385 break;
1386 case SkClipStack::Element::kRect_Type:
1387 element->getRect().round(&ir);
1388 tmpClip.op(ir, element->getOp());
1389 break;
1390 case SkClipStack::Element::kEmpty_Type:
1391 tmpClip.setEmpty();
1392 break;
reed@google.com819c9212011-02-23 18:56:55 +00001393 }
1394 }
1395
reed@google.com6f8f2922011-03-04 22:27:10 +00001396#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001397 // now compare against the current rgn
1398 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001399 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001400#endif
reed@google.com819c9212011-02-23 18:56:55 +00001401}
1402#endif
1403
reed@google.com90c07ea2012-04-13 13:50:27 +00001404void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001405 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001406 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001407
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001408 static const SkRect kEmpty = { 0, 0, 0, 0 };
1409 while ((element = iter.next()) != NULL) {
1410 switch (element->getType()) {
1411 case SkClipStack::Element::kPath_Type:
1412 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1413 break;
1414 case SkClipStack::Element::kRect_Type:
1415 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1416 break;
1417 case SkClipStack::Element::kEmpty_Type:
1418 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1419 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001420 }
1421 }
1422}
1423
reed@google.com5c3d1472011-02-22 19:12:23 +00001424///////////////////////////////////////////////////////////////////////////////
1425
reed@google.com3b3e8952012-08-16 20:53:31 +00001426void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001428
reed@google.com3b3e8952012-08-16 20:53:31 +00001429 if (!this->getClipBounds(&r)) {
1430 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001432 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1433 SkScalarToCompareType(r.fTop),
1434 SkScalarToCompareType(r.fRight),
1435 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 }
1437}
1438
reed@google.com3b3e8952012-08-16 20:53:31 +00001439bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001440
reed@google.com16078632011-12-06 18:56:37 +00001441 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001442 return true;
1443
reed@google.com00177082011-10-12 14:34:30 +00001444 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 return true;
1446 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447
tomhudson@google.com8d430182011-06-06 19:11:19 +00001448 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001449 SkRect dst;
1450 fMCRec->fMatrix->mapRect(&dst, rect);
1451 SkIRect idst;
1452 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001453 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001454 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001455 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001456
reed@android.coma380ae42009-07-21 01:17:02 +00001457 // for speed, do the most likely reject compares first
1458 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1459 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1460 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1461 return true;
1462 }
1463 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1464 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1465 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1466 return true;
1467 }
1468 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
reed@google.com3b3e8952012-08-16 20:53:31 +00001472bool SkCanvas::quickReject(const SkPath& path) const {
1473 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001476static inline int pinIntForScalar(int x) {
1477#ifdef SK_SCALAR_IS_FIXED
1478 if (x < SK_MinS16) {
1479 x = SK_MinS16;
1480 } else if (x > SK_MaxS16) {
1481 x = SK_MaxS16;
1482 }
1483#endif
1484 return x;
1485}
1486
reed@google.com3b3e8952012-08-16 20:53:31 +00001487bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001488 SkIRect ibounds;
1489 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 return false;
1491 }
1492
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001493 SkMatrix inverse;
1494 // if we can't invert the CTM, we can't return local clip bounds
1495 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001496 if (bounds) {
1497 bounds->setEmpty();
1498 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001499 return false;
1500 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001502 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001503 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001504 // adjust it outwards in case we are antialiasing
1505 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001506
1507 // SkRect::iset() will correctly assert if we pass a value out of range
1508 // (when SkScalar==fixed), so we pin to legal values. This does not
1509 // really returnt the correct answer, but its the best we can do given
1510 // that we've promised to return SkRect (even though we support devices
1511 // that can be larger than 32K in width or height).
1512 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1513 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001514 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001515 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516 inverse.mapRect(bounds, r);
1517 }
1518 return true;
1519}
1520
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001521bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001522 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001523 if (clip.isEmpty()) {
1524 if (bounds) {
1525 bounds->setEmpty();
1526 }
1527 return false;
1528 }
1529
1530 if (NULL != bounds) {
1531 *bounds = clip.getBounds();
1532 }
1533 return true;
1534}
1535
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536const SkMatrix& SkCanvas::getTotalMatrix() const {
1537 return *fMCRec->fMatrix;
1538}
1539
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001540SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001541 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1542 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001543 return kComplex_ClipType;
1544}
1545
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001547 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548}
1549
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001550SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001551 int width, int height,
1552 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001553 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001554 if (device) {
1555 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1556 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001557 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001558 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001559 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001560}
1561
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001562SkBaseDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001563 int width, int height,
1564 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001565 SkBaseDevice* device = this->getDevice();
bsalomon@google.come97f0852011-06-17 13:10:25 +00001566 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001567 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001568 } else {
1569 return NULL;
1570 }
1571}
1572
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001573GrContext* SkCanvas::getGrContext() {
1574#if SK_SUPPORT_GPU
1575 SkBaseDevice* device = this->getTopDevice();
1576 if (NULL != device) {
1577 GrRenderTarget* renderTarget = device->accessRenderTarget();
1578 if (NULL != renderTarget) {
1579 return renderTarget->getContext();
1580 }
1581 }
1582#endif
1583
1584 return NULL;
1585
1586}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001587
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588//////////////////////////////////////////////////////////////////////////////
1589// These are the virtual drawing methods
1590//////////////////////////////////////////////////////////////////////////////
1591
reed@google.com2a981812011-04-14 18:59:28 +00001592void SkCanvas::clear(SkColor color) {
1593 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001594 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001595 while (iter.next()) {
1596 iter.fDevice->clear(color);
1597 }
1598}
1599
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001601 this->internalDrawPaint(paint);
1602}
1603
1604void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001605 CHECK_SHADER_NOSETCONTEXT(paint);
1606
reed@google.com4e2b3d32011-04-07 14:18:59 +00001607 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608
1609 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001610 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 }
1612
reed@google.com4e2b3d32011-04-07 14:18:59 +00001613 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614}
1615
1616void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1617 const SkPaint& paint) {
1618 if ((long)count <= 0) {
1619 return;
1620 }
1621
reed@google.comea033602012-12-14 13:13:55 +00001622 CHECK_SHADER_NOSETCONTEXT(paint);
1623
reed@google.coma584aed2012-05-16 14:06:02 +00001624 if (paint.canComputeFastBounds()) {
1625 SkRect r;
1626 // special-case 2 points (common for drawing a single line)
1627 if (2 == count) {
1628 r.set(pts[0], pts[1]);
1629 } else {
1630 r.set(pts, count);
1631 }
1632 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001633 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001634 return;
1635 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001636 }
reed@google.coma584aed2012-05-16 14:06:02 +00001637
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638 SkASSERT(pts != NULL);
1639
reed@google.com4e2b3d32011-04-07 14:18:59 +00001640 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001641
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001643 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644 }
reed@google.com4b226022011-01-11 18:32:13 +00001645
reed@google.com4e2b3d32011-04-07 14:18:59 +00001646 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647}
1648
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001649void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001650 CHECK_SHADER_NOSETCONTEXT(paint);
1651
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 if (paint.canComputeFastBounds()) {
1653 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001654 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 return;
1656 }
1657 }
reed@google.com4b226022011-01-11 18:32:13 +00001658
reed@google.com4e2b3d32011-04-07 14:18:59 +00001659 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660
1661 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001662 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663 }
1664
reed@google.com4e2b3d32011-04-07 14:18:59 +00001665 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001666}
1667
reed@google.com4ed0fb72012-12-12 20:48:18 +00001668void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001669 CHECK_SHADER_NOSETCONTEXT(paint);
1670
reed@google.com4ed0fb72012-12-12 20:48:18 +00001671 if (paint.canComputeFastBounds()) {
1672 SkRect storage;
1673 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1674 return;
1675 }
1676 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001677
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001678 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1679
1680 while (iter.next()) {
1681 iter.fDevice->drawOval(iter, oval, looper.paint());
1682 }
1683
1684 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001685}
1686
1687void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001688 CHECK_SHADER_NOSETCONTEXT(paint);
1689
reed@google.com4ed0fb72012-12-12 20:48:18 +00001690 if (paint.canComputeFastBounds()) {
1691 SkRect storage;
1692 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1693 return;
1694 }
1695 }
1696
1697 if (rrect.isRect()) {
1698 // call the non-virtual version
1699 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001700 return;
1701 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001702 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001703 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1704 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001705 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001706
1707 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1708
1709 while (iter.next()) {
1710 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1711 }
1712
1713 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001714}
1715
1716
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001717void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001718 CHECK_SHADER_NOSETCONTEXT(paint);
1719
reed@google.com93645112012-07-26 16:11:47 +00001720 if (!path.isFinite()) {
1721 return;
1722 }
1723
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001724 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001725 SkRect storage;
1726 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001727 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728 return;
1729 }
1730 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001731 if (path.isEmpty()) {
1732 if (path.isInverseFillType()) {
1733 this->internalDrawPaint(paint);
1734 }
1735 return;
1736 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737
reed@google.com4e2b3d32011-04-07 14:18:59 +00001738 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739
1740 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001741 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742 }
1743
reed@google.com4e2b3d32011-04-07 14:18:59 +00001744 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001745}
1746
1747void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1748 const SkPaint* paint) {
1749 SkDEBUGCODE(bitmap.validate();)
1750
reed@google.com3d608122011-11-21 15:16:16 +00001751 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001752 SkRect bounds = {
1753 x, y,
1754 x + SkIntToScalar(bitmap.width()),
1755 y + SkIntToScalar(bitmap.height())
1756 };
1757 if (paint) {
1758 (void)paint->computeFastBounds(bounds, &bounds);
1759 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001760 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 return;
1762 }
1763 }
reed@google.com4b226022011-01-11 18:32:13 +00001764
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765 SkMatrix matrix;
1766 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001767 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768}
1769
reed@google.com9987ec32011-09-07 11:57:52 +00001770// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001771void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001772 const SkRect& dst, const SkPaint* paint,
1773 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1775 return;
1776 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001777
reed@google.comea033602012-12-14 13:13:55 +00001778 CHECK_LOCKCOUNT_BALANCE(bitmap);
1779
reed@google.com3d608122011-11-21 15:16:16 +00001780 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001781 SkRect storage;
1782 const SkRect* bounds = &dst;
1783 if (paint) {
1784 bounds = &paint->computeFastBounds(dst, &storage);
1785 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001786 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001787 return;
1788 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789 }
reed@google.com3d608122011-11-21 15:16:16 +00001790
reed@google.com33535f32012-09-25 15:37:50 +00001791 SkLazyPaint lazy;
1792 if (NULL == paint) {
1793 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001795
reed@google.com33535f32012-09-25 15:37:50 +00001796 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001797
reed@google.com33535f32012-09-25 15:37:50 +00001798 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001799 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001800 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001801
reed@google.com33535f32012-09-25 15:37:50 +00001802 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803}
1804
reed@google.com71121732012-09-18 15:14:33 +00001805void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001806 const SkRect& dst, const SkPaint* paint,
1807 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001808 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001809 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001810}
1811
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1813 const SkPaint* paint) {
1814 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001815 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816}
1817
reed@google.com9987ec32011-09-07 11:57:52 +00001818void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1819 const SkIRect& center, const SkRect& dst,
1820 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001821 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001822 SkRect storage;
1823 const SkRect* bounds = &dst;
1824 if (paint) {
1825 bounds = &paint->computeFastBounds(dst, &storage);
1826 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001827 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001828 return;
1829 }
1830 }
1831
reed@google.com9987ec32011-09-07 11:57:52 +00001832 const int32_t w = bitmap.width();
1833 const int32_t h = bitmap.height();
1834
1835 SkIRect c = center;
1836 // pin center to the bounds of the bitmap
1837 c.fLeft = SkMax32(0, center.fLeft);
1838 c.fTop = SkMax32(0, center.fTop);
1839 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1840 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1841
reed@google.com71121732012-09-18 15:14:33 +00001842 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001843 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001844 };
1845 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001846 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001847 };
reed@google.com9987ec32011-09-07 11:57:52 +00001848 SkScalar dstX[4] = {
1849 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1850 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1851 };
1852 SkScalar dstY[4] = {
1853 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1854 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1855 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001856
reed@google.com9987ec32011-09-07 11:57:52 +00001857 if (dstX[1] > dstX[2]) {
1858 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1859 dstX[2] = dstX[1];
1860 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001861
reed@google.com9987ec32011-09-07 11:57:52 +00001862 if (dstY[1] > dstY[2]) {
1863 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1864 dstY[2] = dstY[1];
1865 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001866
reed@google.com9987ec32011-09-07 11:57:52 +00001867 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001868 SkRect s, d;
1869
reed@google.com9987ec32011-09-07 11:57:52 +00001870 s.fTop = srcY[y];
1871 s.fBottom = srcY[y+1];
1872 d.fTop = dstY[y];
1873 d.fBottom = dstY[y+1];
1874 for (int x = 0; x < 3; x++) {
1875 s.fLeft = srcX[x];
1876 s.fRight = srcX[x+1];
1877 d.fLeft = dstX[x];
1878 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001879 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001880 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001881 }
1882 }
1883}
1884
1885void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1886 const SkRect& dst, const SkPaint* paint) {
1887 SkDEBUGCODE(bitmap.validate();)
1888
1889 // Need a device entry-point, so gpu can use a mesh
1890 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1891}
1892
reed@google.comf67e4cf2011-03-15 20:56:58 +00001893class SkDeviceFilteredPaint {
1894public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001895 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1896 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001897 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001898 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001899 newPaint->setFlags(flags.fFlags);
1900 newPaint->setHinting(flags.fHinting);
1901 fPaint = newPaint;
1902 } else {
1903 fPaint = &paint;
1904 }
1905 }
1906
reed@google.comf67e4cf2011-03-15 20:56:58 +00001907 const SkPaint& paint() const { return *fPaint; }
1908
1909private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001910 const SkPaint* fPaint;
1911 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001912};
1913
bungeman@google.com52c748b2011-08-22 21:30:43 +00001914void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1915 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001916 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001917 draw.fDevice->drawRect(draw, r, paint);
1918 } else {
1919 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001920 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001921 draw.fDevice->drawRect(draw, r, p);
1922 }
1923}
1924
1925void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1926 const char text[], size_t byteLength,
1927 SkScalar x, SkScalar y) {
1928 SkASSERT(byteLength == 0 || text != NULL);
1929
1930 // nothing to draw
1931 if (text == NULL || byteLength == 0 ||
1932 draw.fClip->isEmpty() ||
1933 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1934 return;
1935 }
1936
1937 SkScalar width = 0;
1938 SkPoint start;
1939
1940 start.set(0, 0); // to avoid warning
1941 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1942 SkPaint::kStrikeThruText_Flag)) {
1943 width = paint.measureText(text, byteLength);
1944
1945 SkScalar offsetX = 0;
1946 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1947 offsetX = SkScalarHalf(width);
1948 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1949 offsetX = width;
1950 }
1951 start.set(x - offsetX, y);
1952 }
1953
1954 if (0 == width) {
1955 return;
1956 }
1957
1958 uint32_t flags = paint.getFlags();
1959
1960 if (flags & (SkPaint::kUnderlineText_Flag |
1961 SkPaint::kStrikeThruText_Flag)) {
1962 SkScalar textSize = paint.getTextSize();
1963 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1964 SkRect r;
1965
1966 r.fLeft = start.fX;
1967 r.fRight = start.fX + width;
1968
1969 if (flags & SkPaint::kUnderlineText_Flag) {
1970 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1971 start.fY);
1972 r.fTop = offset;
1973 r.fBottom = offset + height;
1974 DrawRect(draw, paint, r, textSize);
1975 }
1976 if (flags & SkPaint::kStrikeThruText_Flag) {
1977 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1978 start.fY);
1979 r.fTop = offset;
1980 r.fBottom = offset + height;
1981 DrawRect(draw, paint, r, textSize);
1982 }
1983 }
1984}
1985
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986void SkCanvas::drawText(const void* text, size_t byteLength,
1987 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001988 CHECK_SHADER_NOSETCONTEXT(paint);
1989
reed@google.com4e2b3d32011-04-07 14:18:59 +00001990 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991
1992 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001993 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001994 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001995 DrawTextDecorations(iter, dfp.paint(),
1996 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997 }
1998
reed@google.com4e2b3d32011-04-07 14:18:59 +00001999 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000}
2001
2002void SkCanvas::drawPosText(const void* text, size_t byteLength,
2003 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002004 CHECK_SHADER_NOSETCONTEXT(paint);
2005
reed@google.com4e2b3d32011-04-07 14:18:59 +00002006 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002007
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002009 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002010 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002011 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 }
reed@google.com4b226022011-01-11 18:32:13 +00002013
reed@google.com4e2b3d32011-04-07 14:18:59 +00002014 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002015}
2016
2017void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2018 const SkScalar xpos[], SkScalar constY,
2019 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002020 CHECK_SHADER_NOSETCONTEXT(paint);
2021
reed@google.com4e2b3d32011-04-07 14:18:59 +00002022 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002023
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002025 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002027 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 }
reed@google.com4b226022011-01-11 18:32:13 +00002029
reed@google.com4e2b3d32011-04-07 14:18:59 +00002030 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002031}
2032
2033void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2034 const SkPath& path, const SkMatrix* matrix,
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::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039
2040 while (iter.next()) {
2041 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002042 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043 }
2044
reed@google.com4e2b3d32011-04-07 14:18:59 +00002045 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002046}
2047
2048void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2049 const SkPoint verts[], const SkPoint texs[],
2050 const SkColor colors[], SkXfermode* xmode,
2051 const uint16_t indices[], int indexCount,
2052 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002053 CHECK_SHADER_NOSETCONTEXT(paint);
2054
reed@google.com4e2b3d32011-04-07 14:18:59 +00002055 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002056
reed@android.com8a1c16f2008-12-17 15:59:43 +00002057 while (iter.next()) {
2058 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002059 colors, xmode, indices, indexCount,
2060 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 }
reed@google.com4b226022011-01-11 18:32:13 +00002062
reed@google.com4e2b3d32011-04-07 14:18:59 +00002063 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064}
2065
2066//////////////////////////////////////////////////////////////////////////////
2067// These methods are NOT virtual, and therefore must call back into virtual
2068// methods, rather than actually drawing themselves.
2069//////////////////////////////////////////////////////////////////////////////
2070
2071void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002072 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 SkPaint paint;
2074
2075 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002076 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002077 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078 }
2079 this->drawPaint(paint);
2080}
2081
reed@android.com845fdac2009-06-23 03:01:32 +00002082void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083 SkPaint paint;
2084
2085 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002086 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002087 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088 }
2089 this->drawPaint(paint);
2090}
2091
2092void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2093 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002094
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 pt.set(x, y);
2096 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2097}
2098
2099void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2100 SkPoint pt;
2101 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002102
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 pt.set(x, y);
2104 paint.setColor(color);
2105 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2106}
2107
2108void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2109 const SkPaint& paint) {
2110 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002111
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 pts[0].set(x0, y0);
2113 pts[1].set(x1, y1);
2114 this->drawPoints(kLines_PointMode, 2, pts, paint);
2115}
2116
2117void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2118 SkScalar right, SkScalar bottom,
2119 const SkPaint& paint) {
2120 SkRect r;
2121
2122 r.set(left, top, right, bottom);
2123 this->drawRect(r, paint);
2124}
2125
2126void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2127 const SkPaint& paint) {
2128 if (radius < 0) {
2129 radius = 0;
2130 }
2131
2132 SkRect r;
2133 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002134 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135}
2136
2137void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2138 const SkPaint& paint) {
2139 if (rx > 0 && ry > 0) {
2140 if (paint.canComputeFastBounds()) {
2141 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002142 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143 return;
2144 }
2145 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002146 SkRRect rrect;
2147 rrect.setRectXY(r, rx, ry);
2148 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 } else {
2150 this->drawRect(r, paint);
2151 }
2152}
2153
reed@android.com8a1c16f2008-12-17 15:59:43 +00002154void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2155 SkScalar sweepAngle, bool useCenter,
2156 const SkPaint& paint) {
2157 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2158 this->drawOval(oval, paint);
2159 } else {
2160 SkPath path;
2161 if (useCenter) {
2162 path.moveTo(oval.centerX(), oval.centerY());
2163 }
2164 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2165 if (useCenter) {
2166 path.close();
2167 }
2168 this->drawPath(path, paint);
2169 }
2170}
2171
2172void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2173 const SkPath& path, SkScalar hOffset,
2174 SkScalar vOffset, const SkPaint& paint) {
2175 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002176
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177 matrix.setTranslate(hOffset, vOffset);
2178 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2179}
2180
reed@android.comf76bacf2009-05-13 14:00:33 +00002181///////////////////////////////////////////////////////////////////////////////
2182
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185}
2186
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187///////////////////////////////////////////////////////////////////////////////
2188///////////////////////////////////////////////////////////////////////////////
2189
2190SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002191 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192
2193 SkASSERT(canvas);
2194
2195 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2196 fDone = !fImpl->next();
2197}
2198
2199SkCanvas::LayerIter::~LayerIter() {
2200 fImpl->~SkDrawIter();
2201}
2202
2203void SkCanvas::LayerIter::next() {
2204 fDone = !fImpl->next();
2205}
2206
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002207SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 return fImpl->getDevice();
2209}
2210
2211const SkMatrix& SkCanvas::LayerIter::matrix() const {
2212 return fImpl->getMatrix();
2213}
2214
2215const SkPaint& SkCanvas::LayerIter::paint() const {
2216 const SkPaint* paint = fImpl->getPaint();
2217 if (NULL == paint) {
2218 paint = &fDefaultPaint;
2219 }
2220 return *paint;
2221}
2222
2223const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2224int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2225int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002226
2227///////////////////////////////////////////////////////////////////////////////
2228
2229SkCanvas::ClipVisitor::~ClipVisitor() { }