blob: 7add524260be09811e3ebd8b63726320213e7b90 [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
reed@google.com82ce2b82012-06-26 17:43:26 +000029SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000030SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000031SK_DEFINE_INST_COUNT(SkDrawFilter)
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};
462
463#include "SkColorPriv.h"
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465////////// macros to place around the internal draw calls //////////////////
466
reed@google.com8926b162012-03-23 15:36:36 +0000467#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000468 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000469 AutoDrawLooper looper(this, paint, true); \
470 while (looper.next(type)) { \
471 SkAutoBounderCommit ac(fBounder); \
472 SkDrawIter iter(this);
473
reed@google.com4e2b3d32011-04-07 14:18:59 +0000474#define LOOPER_BEGIN(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000475 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000476 AutoDrawLooper looper(this, paint); \
477 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkAutoBounderCommit ac(fBounder); \
479 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000480
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482
483////////////////////////////////////////////////////////////////////////////
484
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000485SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000487 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000489 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000490 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000491 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000492 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000493 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494
495 fMCRec = (MCRec*)fMCStack.push_back();
496 new (fMCRec) MCRec(NULL, 0);
497
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000498 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 fMCRec->fTopLayer = fMCRec->fLayer;
500 fMCRec->fNext = NULL;
501
reed@google.com97af1a62012-08-28 12:19:02 +0000502 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 return this->setDevice(device);
505}
506
reed@google.comcde92112011-07-06 20:00:52 +0000507SkCanvas::SkCanvas()
508: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000509 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000510
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000511 this->init(NULL);
512}
513
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000514SkCanvas::SkCanvas(SkBaseDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000515 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 inc_canvas();
517
518 this->init(device);
519}
520
521SkCanvas::SkCanvas(const SkBitmap& bitmap)
522 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
523 inc_canvas();
524
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000525 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526}
527
528SkCanvas::~SkCanvas() {
529 // free up the contents of our deque
530 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000531 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 this->internalRestore(); // restore the last, since we're going away
534
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000535 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000536 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 dec_canvas();
539}
540
541SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
542 SkRefCnt_SafeAssign(fBounder, bounder);
543 return bounder;
544}
545
546SkDrawFilter* SkCanvas::getDrawFilter() const {
547 return fMCRec->fFilter;
548}
549
550SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
551 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
552 return filter;
553}
554
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000555SkMetaData& SkCanvas::getMetaData() {
556 // metadata users are rare, so we lazily allocate it. If that changes we
557 // can decide to just make it a field in the device (rather than a ptr)
558 if (NULL == fMetaData) {
559 fMetaData = new SkMetaData;
560 }
561 return *fMetaData;
562}
563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564///////////////////////////////////////////////////////////////////////////////
565
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000566void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000567 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000568 if (device) {
569 device->flush();
570 }
571}
572
reed@google.com210ce002011-11-01 14:24:23 +0000573SkISize SkCanvas::getDeviceSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000574 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000575 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
576}
577
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000578SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000580 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 SkASSERT(rec && rec->fLayer);
582 return rec->fLayer->fDevice;
583}
584
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000585SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000586 if (updateMatrixClip) {
587 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
588 }
reed@google.com9266fed2011-03-30 00:18:03 +0000589 return fMCRec->fTopLayer->fDevice;
590}
591
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000592SkBaseDevice* SkCanvas::setDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000594 SkDeque::F2BIter iter(fMCStack);
595 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000597 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598
599 if (rootDevice == device) {
600 return device;
601 }
reed@google.com4b226022011-01-11 18:32:13 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000604 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 }
606 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000607 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 }
609
610 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
611 rootDevice = device;
612
613 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 /* Now we update our initial region to have the bounds of the new device,
616 and then intersect all of the clips in our stack with these bounds,
617 to ensure that we can't draw outside of the device's bounds (and trash
618 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 NOTE: this is only a partial-fix, since if the new device is larger than
621 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000622 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
624 reconstruct the correct clips, so this approximation will have to do.
625 The caller really needs to restore() back to the base if they want to
626 accurately take advantage of the new device bounds.
627 */
628
reed@google.com42aea282012-03-28 16:19:15 +0000629 SkIRect bounds;
630 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000632 } else {
633 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
reed@google.com42aea282012-03-28 16:19:15 +0000635 // now jam our 1st clip to be bounds, and intersect the rest with that
636 rec->fRasterClip->setRect(bounds);
637 while ((rec = (MCRec*)iter.next()) != NULL) {
638 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
639 }
640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 return device;
642}
643
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000644bool SkCanvas::readPixels(SkBitmap* bitmap,
645 int x, int y,
646 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000647 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000648 if (!device) {
649 return false;
650 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000651 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000652}
653
bsalomon@google.comc6980972011-11-02 19:57:21 +0000654bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000655 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000656 if (!device) {
657 return false;
658 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000659
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000660 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000661 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000662 if (!bounds.intersect(srcRect)) {
663 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000664 }
665
666 SkBitmap tmp;
667 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
668 bounds.height());
669 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
670 bitmap->swap(tmp);
671 return true;
672 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000673 return false;
674 }
reed@google.com51df9e32010-12-23 19:29:18 +0000675}
676
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000677void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
678 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000679 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000680 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000681 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
682 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
683 device->accessBitmap(true);
684 device->writePixels(bitmap, x, y, config8888);
685 }
reed@google.com51df9e32010-12-23 19:29:18 +0000686 }
687}
688
junov@google.com4370aed2012-01-18 16:21:08 +0000689SkCanvas* SkCanvas::canvasForDrawIter() {
690 return this;
691}
692
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693//////////////////////////////////////////////////////////////////////////////
694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695void SkCanvas::updateDeviceCMCache() {
696 if (fDeviceCMDirty) {
697 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000698 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000700
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000702 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000704 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000706 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 } while ((layer = layer->fNext) != NULL);
708 }
709 fDeviceCMDirty = false;
710 }
711}
712
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713///////////////////////////////////////////////////////////////////////////////
714
715int SkCanvas::internalSave(SaveFlags flags) {
716 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000717
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 MCRec* newTop = (MCRec*)fMCStack.push_back();
719 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000720
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 newTop->fNext = fMCRec;
722 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000723
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000724 if (SkCanvas::kClip_SaveFlag & flags) {
725 fClipStack.save();
726 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 return saveCount;
729}
730
731int SkCanvas::save(SaveFlags flags) {
732 // call shared impl
733 return this->internalSave(flags);
734}
735
736#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
737#define C16MASK (1 << SkBitmap::kRGB_565_Config)
738#define C8MASK (1 << SkBitmap::kA8_Config)
739
740static SkBitmap::Config resolve_config(SkCanvas* canvas,
741 const SkIRect& bounds,
742 SkCanvas::SaveFlags flags,
743 bool* isOpaque) {
744 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
745
746#if 0
747 // loop through and union all the configs we may draw into
748 uint32_t configMask = 0;
749 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
750 {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000751 SkBaseDevice* device = canvas->getLayerDevice(i);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 if (device->intersects(bounds))
753 configMask |= 1 << device->config();
754 }
755
756 // if the caller wants alpha or fullcolor, we can't return 565
757 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
758 SkCanvas::kHasAlphaLayer_SaveFlag))
759 configMask &= ~C16MASK;
760
761 switch (configMask) {
762 case C8MASK: // if we only have A8, return that
763 return SkBitmap::kA8_Config;
764
765 case C16MASK: // if we only have 565, return that
766 return SkBitmap::kRGB_565_Config;
767
768 default:
769 return SkBitmap::kARGB_8888_Config; // default answer
770 }
771#else
772 return SkBitmap::kARGB_8888_Config; // default answer
773#endif
774}
775
776static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
777 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
778}
779
junov@chromium.orga907ac32012-02-24 21:54:07 +0000780bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
781 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000782 SkIRect clipBounds;
783 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000784 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000785 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000786 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 if (NULL != bounds) {
788 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 this->getTotalMatrix().mapRect(&r, *bounds);
791 r.roundOut(&ir);
792 // early exit if the layer's bounds are clipped out
793 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000794 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000795 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000796 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000797 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 }
799 } else { // no user bounds, so just use the clip
800 ir = clipBounds;
801 }
802
reed@google.com5c3d1472011-02-22 19:12:23 +0000803 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000804
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 // early exit if the clip is now empty
806 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000807 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000808 return false;
809 }
810
811 if (intersection) {
812 *intersection = ir;
813 }
814 return true;
815}
816
817int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
818 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000819 return this->internalSaveLayer(bounds, paint, flags, false);
820}
821
822int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
823 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000824 // do this before we create the layer. We don't call the public save() since
825 // that would invoke a possibly overridden virtual
826 int count = this->internalSave(flags);
827
828 fDeviceCMDirty = true;
829
830 SkIRect ir;
831 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return count;
833 }
834
reed@google.comb55deeb2012-01-06 14:43:09 +0000835 // Kill the imagefilter if our device doesn't allow it
836 SkLazyPaint lazyP;
837 if (paint && paint->getImageFilter()) {
838 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000839 if (justForImageFilter) {
840 // early exit if the layer was just for the imageFilter
841 return count;
842 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000843 SkPaint* p = lazyP.set(*paint);
844 p->setImageFilter(NULL);
845 paint = p;
846 }
847 }
848
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 bool isOpaque;
850 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
851
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000852 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000853 if (paint && paint->getImageFilter()) {
854 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
855 isOpaque);
856 } else {
857 device = this->createLayerDevice(config, ir.width(), ir.height(),
858 isOpaque);
859 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000860 if (NULL == device) {
861 SkDebugf("Unable to create device for layer.");
862 return count;
863 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000864
reed@google.com6f8f2922011-03-04 22:27:10 +0000865 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000866 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 device->unref();
868
869 layer->fNext = fMCRec->fTopLayer;
870 fMCRec->fLayer = layer;
871 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
872
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000873 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 return count;
875}
876
877int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
878 SaveFlags flags) {
879 if (0xFF == alpha) {
880 return this->saveLayer(bounds, NULL, flags);
881 } else {
882 SkPaint tmpPaint;
883 tmpPaint.setAlpha(alpha);
884 return this->saveLayer(bounds, &tmpPaint, flags);
885 }
886}
887
888void SkCanvas::restore() {
889 // check for underflow
890 if (fMCStack.count() > 1) {
891 this->internalRestore();
892 }
893}
894
895void SkCanvas::internalRestore() {
896 SkASSERT(fMCStack.count() != 0);
897
898 fDeviceCMDirty = true;
899 fLocalBoundsCompareTypeDirty = true;
900
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000901 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
902 fClipStack.restore();
903 }
904
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000905 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 DeviceCM* layer = fMCRec->fLayer; // may be null
907 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
908 fMCRec->fLayer = NULL;
909
910 // now do the normal restore()
911 fMCRec->~MCRec(); // balanced in save()
912 fMCStack.pop_back();
913 fMCRec = (MCRec*)fMCStack.back();
914
915 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
916 since if we're being recorded, we don't want to record this (the
917 recorder will have already recorded the restore).
918 */
919 if (NULL != layer) {
920 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000921 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000922 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
923 layer->fPaint);
924 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000926
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000927 SkASSERT(fSaveLayerCount > 0);
928 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 }
930 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000931 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932}
933
934int SkCanvas::getSaveCount() const {
935 return fMCStack.count();
936}
937
938void SkCanvas::restoreToCount(int count) {
939 // sanity check
940 if (count < 1) {
941 count = 1;
942 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000943
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000944 int n = this->getSaveCount() - count;
945 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 this->restore();
947 }
948}
949
reed@google.com7c202932011-12-14 18:48:05 +0000950bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000951 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000952}
953
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954/////////////////////////////////////////////////////////////////////////////
955
956// can't draw it if its empty, or its too big for a fixed-point width or height
957static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +0000958 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959}
960
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000961void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 const SkMatrix& matrix, const SkPaint* paint) {
963 if (reject_bitmap(bitmap)) {
964 return;
965 }
966
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000967 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000969 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000971
972 SkDEBUGCODE(bitmap.validate();)
973 CHECK_LOCKCOUNT_BALANCE(bitmap);
974
975 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
976
977 while (iter.next()) {
978 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
979 }
980
981 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982}
983
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000984void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +0000985 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 SkPaint tmp;
987 if (NULL == paint) {
988 tmp.setDither(true);
989 paint = &tmp;
990 }
reed@google.com4b226022011-01-11 18:32:13 +0000991
reed@google.com8926b162012-03-23 15:36:36 +0000992 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000994 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000995 paint = &looper.paint();
996 SkImageFilter* filter = paint->getImageFilter();
997 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000998 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000999 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001000 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +00001001 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001002 SkMatrix matrix = *iter.fMatrix;
1003 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1004 if (filter->filterImage(&proxy, src, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001005 SkPaint tmpUnfiltered(*paint);
1006 tmpUnfiltered.setImageFilter(NULL);
1007 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001008 }
1009 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001010 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001011 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001013 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014}
1015
reed@google.com8926b162012-03-23 15:36:36 +00001016void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1017 const SkPaint* paint) {
1018 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001019 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001020
reed@google.com8926b162012-03-23 15:36:36 +00001021 if (reject_bitmap(bitmap)) {
1022 return;
1023 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001024
reed@google.com8926b162012-03-23 15:36:36 +00001025 SkPaint tmp;
1026 if (NULL == paint) {
1027 paint = &tmp;
1028 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001029
reed@google.com8926b162012-03-23 15:36:36 +00001030 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001031
reed@google.com8926b162012-03-23 15:36:36 +00001032 while (iter.next()) {
1033 paint = &looper.paint();
1034 SkImageFilter* filter = paint->getImageFilter();
1035 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1036 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001037 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001038 SkBitmap dst;
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001039 SkMatrix matrix = *iter.fMatrix;
1040 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1041 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001042 SkPaint tmpUnfiltered(*paint);
1043 tmpUnfiltered.setImageFilter(NULL);
1044 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1045 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001046 }
1047 } else {
1048 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1049 }
1050 }
1051 LOOPER_END
1052}
1053
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054/////////////////////////////////////////////////////////////////////////////
1055
1056bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1057 fDeviceCMDirty = true;
1058 fLocalBoundsCompareTypeDirty = true;
1059 return fMCRec->fMatrix->preTranslate(dx, dy);
1060}
1061
1062bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1063 fDeviceCMDirty = true;
1064 fLocalBoundsCompareTypeDirty = true;
1065 return fMCRec->fMatrix->preScale(sx, sy);
1066}
1067
1068bool SkCanvas::rotate(SkScalar degrees) {
1069 fDeviceCMDirty = true;
1070 fLocalBoundsCompareTypeDirty = true;
1071 return fMCRec->fMatrix->preRotate(degrees);
1072}
1073
1074bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1075 fDeviceCMDirty = true;
1076 fLocalBoundsCompareTypeDirty = true;
1077 return fMCRec->fMatrix->preSkew(sx, sy);
1078}
1079
1080bool SkCanvas::concat(const SkMatrix& matrix) {
1081 fDeviceCMDirty = true;
1082 fLocalBoundsCompareTypeDirty = true;
1083 return fMCRec->fMatrix->preConcat(matrix);
1084}
1085
1086void SkCanvas::setMatrix(const SkMatrix& matrix) {
1087 fDeviceCMDirty = true;
1088 fLocalBoundsCompareTypeDirty = true;
1089 *fMCRec->fMatrix = matrix;
1090}
1091
1092// this is not virtual, so it must call a virtual method so that subclasses
1093// will see its action
1094void SkCanvas::resetMatrix() {
1095 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001096
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 matrix.reset();
1098 this->setMatrix(matrix);
1099}
1100
1101//////////////////////////////////////////////////////////////////////////////
1102
reed@google.comc42d35d2011-10-12 11:57:42 +00001103bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001104#ifdef SK_ENABLE_CLIP_QUICKREJECT
1105 if (SkRegion::kIntersect_Op == op) {
1106 if (fMCRec->fRasterClip->isEmpty()) {
1107 return false;
1108 }
1109
reed@google.com3b3e8952012-08-16 20:53:31 +00001110 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001111 fDeviceCMDirty = true;
1112 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001113
1114 fClipStack.clipEmpty();
1115 return fMCRec->fRasterClip->setEmpty();
1116 }
1117 }
1118#endif
1119
reed@google.com5c3d1472011-02-22 19:12:23 +00001120 AutoValidateClip avc(this);
1121
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 fDeviceCMDirty = true;
1123 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001124 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125
1126 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001127 // for these simpler matrices, we can stay a rect ever after applying
1128 // the matrix. This means we don't have to a) make a path, and b) tell
1129 // the region code to scan-convert the path, only to discover that it
1130 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132
1133 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001134 fClipStack.clipDevRect(r, op, doAA);
1135 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001137 // since we're rotate or some such thing, we convert the rect to a path
1138 // and clip against that, since it can handle any matrix. However, to
1139 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1140 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 SkPath path;
1142
1143 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001144 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 }
1146}
1147
reed@google.com00177082011-10-12 14:34:30 +00001148static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001149 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001150 // base is used to limit the size (and therefore memory allocation) of the
1151 // region that results from scan converting devPath.
1152 SkRegion base;
1153
reed@google.com819c9212011-02-23 18:56:55 +00001154 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001155 // since we are intersect, we can do better (tighter) with currRgn's
1156 // bounds, than just using the device. However, if currRgn is complex,
1157 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001158 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001159 // FIXME: we should also be able to do this when currClip->isBW(),
1160 // but relaxing the test above triggers GM asserts in
1161 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1162 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001163 } else {
reed@google.com00177082011-10-12 14:34:30 +00001164 base.setRect(currClip->getBounds());
1165 SkRasterClip clip;
1166 clip.setPath(devPath, base, doAA);
1167 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001168 }
reed@google.com819c9212011-02-23 18:56:55 +00001169 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001170 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001171 if (!device) {
1172 return currClip->setEmpty();
1173 }
1174
junov@chromium.orga907ac32012-02-24 21:54:07 +00001175 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001176
1177 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001178 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001179 } else {
reed@google.com00177082011-10-12 14:34:30 +00001180 SkRasterClip clip;
1181 clip.setPath(devPath, base, doAA);
1182 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001183 }
1184 }
1185}
1186
reed@google.com4ed0fb72012-12-12 20:48:18 +00001187bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1188 if (rrect.isRect()) {
1189 // call the non-virtual version
1190 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1191 } else {
1192 SkPath path;
1193 path.addRRect(rrect);
1194 // call the non-virtual version
1195 return this->SkCanvas::clipPath(path, op, doAA);
1196 }
1197}
1198
reed@google.comc42d35d2011-10-12 11:57:42 +00001199bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001200#ifdef SK_ENABLE_CLIP_QUICKREJECT
1201 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1202 if (fMCRec->fRasterClip->isEmpty()) {
1203 return false;
1204 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001205
reed@google.com3b3e8952012-08-16 20:53:31 +00001206 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001207 fDeviceCMDirty = true;
1208 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001209
reed@google.comda17f752012-08-16 18:27:05 +00001210 fClipStack.clipEmpty();
1211 return fMCRec->fRasterClip->setEmpty();
1212 }
1213 }
1214#endif
1215
reed@google.com5c3d1472011-02-22 19:12:23 +00001216 AutoValidateClip avc(this);
1217
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218 fDeviceCMDirty = true;
1219 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001220 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221
1222 SkPath devPath;
1223 path.transform(*fMCRec->fMatrix, &devPath);
1224
reed@google.comfe701122011-11-08 19:41:23 +00001225 // Check if the transfomation, or the original path itself
1226 // made us empty. Note this can also happen if we contained NaN
1227 // values. computing the bounds detects this, and will set our
1228 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1229 if (devPath.getBounds().isEmpty()) {
1230 // resetting the path will remove any NaN or other wanky values
1231 // that might upset our scan converter.
1232 devPath.reset();
1233 }
1234
reed@google.com5c3d1472011-02-22 19:12:23 +00001235 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001236 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001237
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001238 if (fAllowSimplifyClip) {
1239 devPath.reset();
1240 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1241 const SkClipStack* clipStack = getClipStack();
1242 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1243 const SkClipStack::Element* element;
1244 while ((element = iter.next())) {
1245 SkClipStack::Element::Type type = element->getType();
1246 if (type == SkClipStack::Element::kEmpty_Type) {
1247 continue;
1248 }
1249 SkPath operand;
1250 if (type == SkClipStack::Element::kRect_Type) {
1251 operand.addRect(element->getRect());
1252 } else if (type == SkClipStack::Element::kPath_Type) {
1253 operand = element->getPath();
1254 } else {
1255 SkDEBUGFAIL("Unexpected type.");
1256 }
1257 SkRegion::Op elementOp = element->getOp();
1258 if (elementOp == SkRegion::kReplace_Op) {
1259 devPath = operand;
1260 } else {
1261 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1262 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001263 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1264 // perhaps we need an API change to avoid this sort of mixed-signals about
1265 // clipping.
1266 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001267 }
1268 op = SkRegion::kReplace_Op;
1269 }
1270
reed@google.com00177082011-10-12 14:34:30 +00001271 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272}
1273
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001274bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1275 bool inverseFilled) {
1276 // This is for updating the clip conservatively using only bounds
1277 // information.
1278 // Contract:
1279 // The current clip must contain the true clip. The true
1280 // clip is the clip that would have normally been computed
1281 // by calls to clipPath and clipRRect
1282 // Objective:
1283 // Keep the current clip as small as possible without
1284 // breaking the contract, using only clip bounding rectangles
1285 // (for performance).
1286
1287 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1288 // don't have to worry about getting caught in a loop. Thus anywhere
1289 // we call a virtual method, we explicitly prefix it with
1290 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001291
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001292 if (inverseFilled) {
1293 switch (op) {
1294 case SkRegion::kIntersect_Op:
1295 case SkRegion::kDifference_Op:
1296 // These ops can only shrink the current clip. So leaving
1297 // the clip unchanges conservatively respects the contract.
1298 return this->getClipDeviceBounds(NULL);
1299 case SkRegion::kUnion_Op:
1300 case SkRegion::kReplace_Op:
1301 case SkRegion::kReverseDifference_Op:
1302 case SkRegion::kXOR_Op:
1303 {
1304 // These ops can grow the current clip up to the extents of
1305 // the input clip, which is inverse filled, so we just set
1306 // the current clip to the device bounds.
1307 SkRect deviceBounds;
1308 SkIRect deviceIBounds;
1309 this->getDevice()->getGlobalBounds(&deviceIBounds);
1310 deviceBounds = SkRect::MakeFromIRect(deviceIBounds);
1311 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1312 // set the clip in device space
1313 this->SkCanvas::setMatrix(SkMatrix::I());
1314 bool result = this->SkCanvas::clipRect(deviceBounds,
1315 SkRegion::kReplace_Op, false);
1316 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1317 return result;
1318 }
1319 default:
1320 SkASSERT(0); // unhandled op?
1321 }
1322 } else {
1323 // Not inverse filled
1324 switch (op) {
1325 case SkRegion::kIntersect_Op:
1326 case SkRegion::kUnion_Op:
1327 case SkRegion::kReplace_Op:
1328 return this->SkCanvas::clipRect(bounds, op, false);
1329 case SkRegion::kDifference_Op:
1330 // Difference can only shrink the current clip.
1331 // Leaving clip unchanged conservatively fullfills the contract.
1332 return this->getClipDeviceBounds(NULL);
1333 case SkRegion::kReverseDifference_Op:
1334 // To reverse, we swap in the bounds with a replace op.
1335 // As with difference, leave it unchanged.
1336 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1337 case SkRegion::kXOR_Op:
1338 // Be conservative, based on (A XOR B) always included in (A union B),
1339 // which is always included in (bounds(A) union bounds(B))
1340 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1341 default:
1342 SkASSERT(0); // unhandled op?
1343 }
1344 }
1345 return true;
1346}
1347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001349 AutoValidateClip avc(this);
1350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 fDeviceCMDirty = true;
1352 fLocalBoundsCompareTypeDirty = true;
1353
reed@google.com5c3d1472011-02-22 19:12:23 +00001354 // todo: signal fClipStack that we have a region, and therefore (I guess)
1355 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001356 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001357
reed@google.com00177082011-10-12 14:34:30 +00001358 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359}
1360
reed@google.com819c9212011-02-23 18:56:55 +00001361#ifdef SK_DEBUG
1362void SkCanvas::validateClip() const {
1363 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001364 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001365 if (!device) {
1366 SkASSERT(this->getTotalClip().isEmpty());
1367 return;
1368 }
1369
reed@google.com819c9212011-02-23 18:56:55 +00001370 SkIRect ir;
1371 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001372 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001373
robertphillips@google.com80214e22012-07-20 15:33:18 +00001374 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001375 const SkClipStack::Element* element;
1376 while ((element = iter.next()) != NULL) {
1377 switch (element->getType()) {
1378 case SkClipStack::Element::kPath_Type:
1379 clipPathHelper(this,
1380 &tmpClip,
1381 element->getPath(),
1382 element->getOp(),
1383 element->isAA());
1384 break;
1385 case SkClipStack::Element::kRect_Type:
1386 element->getRect().round(&ir);
1387 tmpClip.op(ir, element->getOp());
1388 break;
1389 case SkClipStack::Element::kEmpty_Type:
1390 tmpClip.setEmpty();
1391 break;
reed@google.com819c9212011-02-23 18:56:55 +00001392 }
1393 }
1394
reed@google.com6f8f2922011-03-04 22:27:10 +00001395#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001396 // now compare against the current rgn
1397 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001398 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001399#endif
reed@google.com819c9212011-02-23 18:56:55 +00001400}
1401#endif
1402
reed@google.com90c07ea2012-04-13 13:50:27 +00001403void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001404 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001405 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001406
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001407 static const SkRect kEmpty = { 0, 0, 0, 0 };
1408 while ((element = iter.next()) != NULL) {
1409 switch (element->getType()) {
1410 case SkClipStack::Element::kPath_Type:
1411 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1412 break;
1413 case SkClipStack::Element::kRect_Type:
1414 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1415 break;
1416 case SkClipStack::Element::kEmpty_Type:
1417 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1418 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001419 }
1420 }
1421}
1422
reed@google.com5c3d1472011-02-22 19:12:23 +00001423///////////////////////////////////////////////////////////////////////////////
1424
reed@google.com3b3e8952012-08-16 20:53:31 +00001425void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001427
reed@google.com3b3e8952012-08-16 20:53:31 +00001428 if (!this->getClipBounds(&r)) {
1429 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001431 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1432 SkScalarToCompareType(r.fTop),
1433 SkScalarToCompareType(r.fRight),
1434 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 }
1436}
1437
reed@google.com3b3e8952012-08-16 20:53:31 +00001438bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001439
reed@google.com16078632011-12-06 18:56:37 +00001440 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001441 return true;
1442
reed@google.com00177082011-10-12 14:34:30 +00001443 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 return true;
1445 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446
tomhudson@google.com8d430182011-06-06 19:11:19 +00001447 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001448 SkRect dst;
1449 fMCRec->fMatrix->mapRect(&dst, rect);
1450 SkIRect idst;
1451 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001452 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001453 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001454 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001455
reed@android.coma380ae42009-07-21 01:17:02 +00001456 // for speed, do the most likely reject compares first
1457 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1458 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1459 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1460 return true;
1461 }
1462 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1463 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1464 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1465 return true;
1466 }
1467 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469}
1470
reed@google.com3b3e8952012-08-16 20:53:31 +00001471bool SkCanvas::quickReject(const SkPath& path) const {
1472 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473}
1474
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001475static inline int pinIntForScalar(int x) {
1476#ifdef SK_SCALAR_IS_FIXED
1477 if (x < SK_MinS16) {
1478 x = SK_MinS16;
1479 } else if (x > SK_MaxS16) {
1480 x = SK_MaxS16;
1481 }
1482#endif
1483 return x;
1484}
1485
reed@google.com3b3e8952012-08-16 20:53:31 +00001486bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001487 SkIRect ibounds;
1488 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 return false;
1490 }
1491
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001492 SkMatrix inverse;
1493 // if we can't invert the CTM, we can't return local clip bounds
1494 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001495 if (bounds) {
1496 bounds->setEmpty();
1497 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001498 return false;
1499 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001501 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001502 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001503 // adjust it outwards in case we are antialiasing
1504 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001505
1506 // SkRect::iset() will correctly assert if we pass a value out of range
1507 // (when SkScalar==fixed), so we pin to legal values. This does not
1508 // really returnt the correct answer, but its the best we can do given
1509 // that we've promised to return SkRect (even though we support devices
1510 // that can be larger than 32K in width or height).
1511 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1512 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001513 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001514 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515 inverse.mapRect(bounds, r);
1516 }
1517 return true;
1518}
1519
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001520bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001521 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001522 if (clip.isEmpty()) {
1523 if (bounds) {
1524 bounds->setEmpty();
1525 }
1526 return false;
1527 }
1528
1529 if (NULL != bounds) {
1530 *bounds = clip.getBounds();
1531 }
1532 return true;
1533}
1534
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535const SkMatrix& SkCanvas::getTotalMatrix() const {
1536 return *fMCRec->fMatrix;
1537}
1538
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001539SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001540 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1541 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001542 return kComplex_ClipType;
1543}
1544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001546 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547}
1548
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001549SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001550 int width, int height,
1551 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001552 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001553 if (device) {
1554 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1555 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001556 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001557 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001558 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559}
1560
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001561SkBaseDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001562 int width, int height,
1563 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001564 SkBaseDevice* device = this->getDevice();
bsalomon@google.come97f0852011-06-17 13:10:25 +00001565 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001566 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001567 } else {
1568 return NULL;
1569 }
1570}
1571
1572
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573//////////////////////////////////////////////////////////////////////////////
1574// These are the virtual drawing methods
1575//////////////////////////////////////////////////////////////////////////////
1576
reed@google.com2a981812011-04-14 18:59:28 +00001577void SkCanvas::clear(SkColor color) {
1578 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001579 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001580 while (iter.next()) {
1581 iter.fDevice->clear(color);
1582 }
1583}
1584
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001586 this->internalDrawPaint(paint);
1587}
1588
1589void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001590 CHECK_SHADER_NOSETCONTEXT(paint);
1591
reed@google.com4e2b3d32011-04-07 14:18:59 +00001592 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593
1594 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001595 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596 }
1597
reed@google.com4e2b3d32011-04-07 14:18:59 +00001598 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599}
1600
1601void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1602 const SkPaint& paint) {
1603 if ((long)count <= 0) {
1604 return;
1605 }
1606
reed@google.comea033602012-12-14 13:13:55 +00001607 CHECK_SHADER_NOSETCONTEXT(paint);
1608
reed@google.coma584aed2012-05-16 14:06:02 +00001609 if (paint.canComputeFastBounds()) {
1610 SkRect r;
1611 // special-case 2 points (common for drawing a single line)
1612 if (2 == count) {
1613 r.set(pts[0], pts[1]);
1614 } else {
1615 r.set(pts, count);
1616 }
1617 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001618 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001619 return;
1620 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001621 }
reed@google.coma584aed2012-05-16 14:06:02 +00001622
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623 SkASSERT(pts != NULL);
1624
reed@google.com4e2b3d32011-04-07 14:18:59 +00001625 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001626
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001628 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 }
reed@google.com4b226022011-01-11 18:32:13 +00001630
reed@google.com4e2b3d32011-04-07 14:18:59 +00001631 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632}
1633
1634void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001635 CHECK_SHADER_NOSETCONTEXT(paint);
1636
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637 if (paint.canComputeFastBounds()) {
1638 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001639 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640 return;
1641 }
1642 }
reed@google.com4b226022011-01-11 18:32:13 +00001643
reed@google.com4e2b3d32011-04-07 14:18:59 +00001644 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645
1646 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001647 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648 }
1649
reed@google.com4e2b3d32011-04-07 14:18:59 +00001650 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651}
1652
reed@google.com4ed0fb72012-12-12 20:48:18 +00001653void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001654 CHECK_SHADER_NOSETCONTEXT(paint);
1655
reed@google.com4ed0fb72012-12-12 20:48:18 +00001656 if (paint.canComputeFastBounds()) {
1657 SkRect storage;
1658 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1659 return;
1660 }
1661 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001662
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001663 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1664
1665 while (iter.next()) {
1666 iter.fDevice->drawOval(iter, oval, looper.paint());
1667 }
1668
1669 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001670}
1671
1672void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001673 CHECK_SHADER_NOSETCONTEXT(paint);
1674
reed@google.com4ed0fb72012-12-12 20:48:18 +00001675 if (paint.canComputeFastBounds()) {
1676 SkRect storage;
1677 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1678 return;
1679 }
1680 }
1681
1682 if (rrect.isRect()) {
1683 // call the non-virtual version
1684 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001685 return;
1686 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001687 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001688 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1689 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001690 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001691
1692 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1693
1694 while (iter.next()) {
1695 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1696 }
1697
1698 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001699}
1700
1701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001703 CHECK_SHADER_NOSETCONTEXT(paint);
1704
reed@google.com93645112012-07-26 16:11:47 +00001705 if (!path.isFinite()) {
1706 return;
1707 }
1708
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001709 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001710 SkRect storage;
1711 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001712 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713 return;
1714 }
1715 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001716 if (path.isEmpty()) {
1717 if (path.isInverseFillType()) {
1718 this->internalDrawPaint(paint);
1719 }
1720 return;
1721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722
reed@google.com4e2b3d32011-04-07 14:18:59 +00001723 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724
1725 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001726 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727 }
1728
reed@google.com4e2b3d32011-04-07 14:18:59 +00001729 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730}
1731
1732void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1733 const SkPaint* paint) {
1734 SkDEBUGCODE(bitmap.validate();)
1735
reed@google.com3d608122011-11-21 15:16:16 +00001736 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001737 SkRect bounds = {
1738 x, y,
1739 x + SkIntToScalar(bitmap.width()),
1740 y + SkIntToScalar(bitmap.height())
1741 };
1742 if (paint) {
1743 (void)paint->computeFastBounds(bounds, &bounds);
1744 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001745 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746 return;
1747 }
1748 }
reed@google.com4b226022011-01-11 18:32:13 +00001749
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750 SkMatrix matrix;
1751 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001752 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753}
1754
reed@google.com9987ec32011-09-07 11:57:52 +00001755// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001756void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001757 const SkRect& dst, const SkPaint* paint,
1758 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1760 return;
1761 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001762
reed@google.comea033602012-12-14 13:13:55 +00001763 CHECK_LOCKCOUNT_BALANCE(bitmap);
1764
reed@google.com3d608122011-11-21 15:16:16 +00001765 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001766 SkRect storage;
1767 const SkRect* bounds = &dst;
1768 if (paint) {
1769 bounds = &paint->computeFastBounds(dst, &storage);
1770 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001771 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001772 return;
1773 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 }
reed@google.com3d608122011-11-21 15:16:16 +00001775
reed@google.com33535f32012-09-25 15:37:50 +00001776 SkLazyPaint lazy;
1777 if (NULL == paint) {
1778 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001780
reed@google.com33535f32012-09-25 15:37:50 +00001781 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001782
reed@google.com33535f32012-09-25 15:37:50 +00001783 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001784 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001785 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001786
reed@google.com33535f32012-09-25 15:37:50 +00001787 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788}
1789
reed@google.com71121732012-09-18 15:14:33 +00001790void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001791 const SkRect& dst, const SkPaint* paint,
1792 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001793 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001794 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001795}
1796
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1798 const SkPaint* paint) {
1799 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001800 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801}
1802
reed@google.com9987ec32011-09-07 11:57:52 +00001803void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1804 const SkIRect& center, const SkRect& dst,
1805 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001806 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001807 SkRect storage;
1808 const SkRect* bounds = &dst;
1809 if (paint) {
1810 bounds = &paint->computeFastBounds(dst, &storage);
1811 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001812 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001813 return;
1814 }
1815 }
1816
reed@google.com9987ec32011-09-07 11:57:52 +00001817 const int32_t w = bitmap.width();
1818 const int32_t h = bitmap.height();
1819
1820 SkIRect c = center;
1821 // pin center to the bounds of the bitmap
1822 c.fLeft = SkMax32(0, center.fLeft);
1823 c.fTop = SkMax32(0, center.fTop);
1824 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1825 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1826
reed@google.com71121732012-09-18 15:14:33 +00001827 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001828 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001829 };
1830 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001831 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001832 };
reed@google.com9987ec32011-09-07 11:57:52 +00001833 SkScalar dstX[4] = {
1834 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1835 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1836 };
1837 SkScalar dstY[4] = {
1838 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1839 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1840 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001841
reed@google.com9987ec32011-09-07 11:57:52 +00001842 if (dstX[1] > dstX[2]) {
1843 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1844 dstX[2] = dstX[1];
1845 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001846
reed@google.com9987ec32011-09-07 11:57:52 +00001847 if (dstY[1] > dstY[2]) {
1848 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1849 dstY[2] = dstY[1];
1850 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001851
reed@google.com9987ec32011-09-07 11:57:52 +00001852 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001853 SkRect s, d;
1854
reed@google.com9987ec32011-09-07 11:57:52 +00001855 s.fTop = srcY[y];
1856 s.fBottom = srcY[y+1];
1857 d.fTop = dstY[y];
1858 d.fBottom = dstY[y+1];
1859 for (int x = 0; x < 3; x++) {
1860 s.fLeft = srcX[x];
1861 s.fRight = srcX[x+1];
1862 d.fLeft = dstX[x];
1863 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001864 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001865 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001866 }
1867 }
1868}
1869
1870void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1871 const SkRect& dst, const SkPaint* paint) {
1872 SkDEBUGCODE(bitmap.validate();)
1873
1874 // Need a device entry-point, so gpu can use a mesh
1875 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1876}
1877
reed@google.comf67e4cf2011-03-15 20:56:58 +00001878class SkDeviceFilteredPaint {
1879public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001880 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1881 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001882 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001883 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001884 newPaint->setFlags(flags.fFlags);
1885 newPaint->setHinting(flags.fHinting);
1886 fPaint = newPaint;
1887 } else {
1888 fPaint = &paint;
1889 }
1890 }
1891
reed@google.comf67e4cf2011-03-15 20:56:58 +00001892 const SkPaint& paint() const { return *fPaint; }
1893
1894private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001895 const SkPaint* fPaint;
1896 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001897};
1898
bungeman@google.com52c748b2011-08-22 21:30:43 +00001899void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1900 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001901 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001902 draw.fDevice->drawRect(draw, r, paint);
1903 } else {
1904 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001905 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001906 draw.fDevice->drawRect(draw, r, p);
1907 }
1908}
1909
1910void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1911 const char text[], size_t byteLength,
1912 SkScalar x, SkScalar y) {
1913 SkASSERT(byteLength == 0 || text != NULL);
1914
1915 // nothing to draw
1916 if (text == NULL || byteLength == 0 ||
1917 draw.fClip->isEmpty() ||
1918 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1919 return;
1920 }
1921
1922 SkScalar width = 0;
1923 SkPoint start;
1924
1925 start.set(0, 0); // to avoid warning
1926 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1927 SkPaint::kStrikeThruText_Flag)) {
1928 width = paint.measureText(text, byteLength);
1929
1930 SkScalar offsetX = 0;
1931 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1932 offsetX = SkScalarHalf(width);
1933 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1934 offsetX = width;
1935 }
1936 start.set(x - offsetX, y);
1937 }
1938
1939 if (0 == width) {
1940 return;
1941 }
1942
1943 uint32_t flags = paint.getFlags();
1944
1945 if (flags & (SkPaint::kUnderlineText_Flag |
1946 SkPaint::kStrikeThruText_Flag)) {
1947 SkScalar textSize = paint.getTextSize();
1948 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1949 SkRect r;
1950
1951 r.fLeft = start.fX;
1952 r.fRight = start.fX + width;
1953
1954 if (flags & SkPaint::kUnderlineText_Flag) {
1955 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1956 start.fY);
1957 r.fTop = offset;
1958 r.fBottom = offset + height;
1959 DrawRect(draw, paint, r, textSize);
1960 }
1961 if (flags & SkPaint::kStrikeThruText_Flag) {
1962 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1963 start.fY);
1964 r.fTop = offset;
1965 r.fBottom = offset + height;
1966 DrawRect(draw, paint, r, textSize);
1967 }
1968 }
1969}
1970
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971void SkCanvas::drawText(const void* text, size_t byteLength,
1972 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001973 CHECK_SHADER_NOSETCONTEXT(paint);
1974
reed@google.com4e2b3d32011-04-07 14:18:59 +00001975 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976
1977 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001978 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001979 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001980 DrawTextDecorations(iter, dfp.paint(),
1981 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982 }
1983
reed@google.com4e2b3d32011-04-07 14:18:59 +00001984 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985}
1986
1987void SkCanvas::drawPosText(const void* text, size_t byteLength,
1988 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001989 CHECK_SHADER_NOSETCONTEXT(paint);
1990
reed@google.com4e2b3d32011-04-07 14:18:59 +00001991 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001992
reed@android.com8a1c16f2008-12-17 15:59:43 +00001993 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001994 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001996 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997 }
reed@google.com4b226022011-01-11 18:32:13 +00001998
reed@google.com4e2b3d32011-04-07 14:18:59 +00001999 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000}
2001
2002void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2003 const SkScalar xpos[], SkScalar constY,
2004 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002005 CHECK_SHADER_NOSETCONTEXT(paint);
2006
reed@google.com4e2b3d32011-04-07 14:18:59 +00002007 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002008
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002010 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002012 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002013 }
reed@google.com4b226022011-01-11 18:32:13 +00002014
reed@google.com4e2b3d32011-04-07 14:18:59 +00002015 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016}
2017
2018void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2019 const SkPath& path, const SkMatrix* matrix,
2020 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002021 CHECK_SHADER_NOSETCONTEXT(paint);
2022
reed@google.com4e2b3d32011-04-07 14:18:59 +00002023 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024
2025 while (iter.next()) {
2026 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002027 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 }
2029
reed@google.com4e2b3d32011-04-07 14:18:59 +00002030 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002031}
2032
djsollen@google.com56c69772011-11-08 19:00:26 +00002033#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002034void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
2035 const SkPoint pos[], const SkPaint& paint,
2036 const SkPath& path, const SkMatrix* matrix) {
reed@google.comea033602012-12-14 13:13:55 +00002037 CHECK_SHADER_NOSETCONTEXT(paint);
2038
reed@google.com4e2b3d32011-04-07 14:18:59 +00002039 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002040
2041 while (iter.next()) {
2042 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002043 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002044 }
2045
reed@google.com4e2b3d32011-04-07 14:18:59 +00002046 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002047}
2048#endif
2049
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2051 const SkPoint verts[], const SkPoint texs[],
2052 const SkColor colors[], SkXfermode* xmode,
2053 const uint16_t indices[], int indexCount,
2054 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002055 CHECK_SHADER_NOSETCONTEXT(paint);
2056
reed@google.com4e2b3d32011-04-07 14:18:59 +00002057 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002058
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059 while (iter.next()) {
2060 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002061 colors, xmode, indices, indexCount,
2062 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063 }
reed@google.com4b226022011-01-11 18:32:13 +00002064
reed@google.com4e2b3d32011-04-07 14:18:59 +00002065 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066}
2067
2068//////////////////////////////////////////////////////////////////////////////
2069// These methods are NOT virtual, and therefore must call back into virtual
2070// methods, rather than actually drawing themselves.
2071//////////////////////////////////////////////////////////////////////////////
2072
2073void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002074 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 SkPaint paint;
2076
2077 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002078 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002079 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080 }
2081 this->drawPaint(paint);
2082}
2083
reed@android.com845fdac2009-06-23 03:01:32 +00002084void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 SkPaint paint;
2086
2087 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002088 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002089 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 }
2091 this->drawPaint(paint);
2092}
2093
2094void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2095 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002096
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097 pt.set(x, y);
2098 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2099}
2100
2101void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2102 SkPoint pt;
2103 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002104
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105 pt.set(x, y);
2106 paint.setColor(color);
2107 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2108}
2109
2110void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2111 const SkPaint& paint) {
2112 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002113
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 pts[0].set(x0, y0);
2115 pts[1].set(x1, y1);
2116 this->drawPoints(kLines_PointMode, 2, pts, paint);
2117}
2118
2119void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2120 SkScalar right, SkScalar bottom,
2121 const SkPaint& paint) {
2122 SkRect r;
2123
2124 r.set(left, top, right, bottom);
2125 this->drawRect(r, paint);
2126}
2127
2128void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2129 const SkPaint& paint) {
2130 if (radius < 0) {
2131 radius = 0;
2132 }
2133
2134 SkRect r;
2135 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002136 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137}
2138
2139void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2140 const SkPaint& paint) {
2141 if (rx > 0 && ry > 0) {
2142 if (paint.canComputeFastBounds()) {
2143 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002144 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145 return;
2146 }
2147 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002148 SkRRect rrect;
2149 rrect.setRectXY(r, rx, ry);
2150 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151 } else {
2152 this->drawRect(r, paint);
2153 }
2154}
2155
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2157 SkScalar sweepAngle, bool useCenter,
2158 const SkPaint& paint) {
2159 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2160 this->drawOval(oval, paint);
2161 } else {
2162 SkPath path;
2163 if (useCenter) {
2164 path.moveTo(oval.centerX(), oval.centerY());
2165 }
2166 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2167 if (useCenter) {
2168 path.close();
2169 }
2170 this->drawPath(path, paint);
2171 }
2172}
2173
2174void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2175 const SkPath& path, SkScalar hOffset,
2176 SkScalar vOffset, const SkPaint& paint) {
2177 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002178
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179 matrix.setTranslate(hOffset, vOffset);
2180 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2181}
2182
reed@android.comf76bacf2009-05-13 14:00:33 +00002183///////////////////////////////////////////////////////////////////////////////
2184
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187}
2188
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189///////////////////////////////////////////////////////////////////////////////
2190///////////////////////////////////////////////////////////////////////////////
2191
2192SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002193 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194
2195 SkASSERT(canvas);
2196
2197 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2198 fDone = !fImpl->next();
2199}
2200
2201SkCanvas::LayerIter::~LayerIter() {
2202 fImpl->~SkDrawIter();
2203}
2204
2205void SkCanvas::LayerIter::next() {
2206 fDone = !fImpl->next();
2207}
2208
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002209SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210 return fImpl->getDevice();
2211}
2212
2213const SkMatrix& SkCanvas::LayerIter::matrix() const {
2214 return fImpl->getMatrix();
2215}
2216
2217const SkPaint& SkCanvas::LayerIter::paint() const {
2218 const SkPaint* paint = fImpl->getPaint();
2219 if (NULL == paint) {
2220 paint = &fDefaultPaint;
2221 }
2222 return *paint;
2223}
2224
2225const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2226int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2227int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002228
2229///////////////////////////////////////////////////////////////////////////////
2230
2231SkCanvas::ClipVisitor::~ClipVisitor() { }