blob: 4eaea11b87e582848b944ac31701d05156cb0db4 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkBounder.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.com82ce2b82012-06-26 17:43:26 +000033SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000034SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000035SK_DEFINE_INST_COUNT(SkDrawFilter)
36
reed@google.comda17f752012-08-16 18:27:05 +000037// experimental for faster tiled drawing...
38//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000039
reed@android.com8a1c16f2008-12-17 15:59:43 +000040//#define SK_TRACE_SAVERESTORE
41
42#ifdef SK_TRACE_SAVERESTORE
43 static int gLayerCounter;
44 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
45 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
46
47 static int gRecCounter;
48 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
49 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
50
51 static int gCanvasCounter;
52 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
53 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
54#else
55 #define inc_layer()
56 #define dec_layer()
57 #define inc_rec()
58 #define dec_rec()
59 #define inc_canvas()
60 #define dec_canvas()
61#endif
62
reed@google.comea033602012-12-14 13:13:55 +000063#ifdef SK_DEBUG
64#include "SkPixelRef.h"
65
reed@google.comf53d0a92013-01-30 13:17:32 +000066/*
67 * Some pixelref subclasses can support being "locked" from another thread
68 * during the lock-scope of skia calling them. In these instances, this balance
69 * check will fail, but may not be indicative of a problem, so we allow a build
70 * flag to disable this check.
71 *
72 * Potentially another fix would be to have a (debug-only) virtual or flag on
73 * pixelref, which could tell us at runtime if this check is valid. That would
74 * eliminate the need for this heavy-handed build check.
75 */
76#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
77class AutoCheckLockCountBalance {
78public:
79 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
80};
81#else
reed@google.comea033602012-12-14 13:13:55 +000082class AutoCheckLockCountBalance {
83public:
84 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
85 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
86 }
87 ~AutoCheckLockCountBalance() {
88 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
89 SkASSERT(count == fLockCount);
90 }
91
92private:
93 const SkPixelRef* fPixelRef;
94 int fLockCount;
95};
reed@google.comf53d0a92013-01-30 13:17:32 +000096#endif
reed@google.comea033602012-12-14 13:13:55 +000097
98class AutoCheckNoSetContext {
99public:
100 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
101 this->assertNoSetContext(fPaint);
102 }
103 ~AutoCheckNoSetContext() {
104 this->assertNoSetContext(fPaint);
105 }
106
107private:
108 const SkPaint& fPaint;
109
110 void assertNoSetContext(const SkPaint& paint) {
111 SkShader* s = paint.getShader();
112 if (s) {
113 SkASSERT(!s->setContextHasBeenCalled());
114 }
115 }
116};
117
118#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
119#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
120
121#else
122 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
123 #define CHECK_SHADER_NOSETCONTEXT(paint)
124#endif
125
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000126typedef SkTLazy<SkPaint> SkLazyPaint;
127
reed@google.com97af1a62012-08-28 12:19:02 +0000128void SkCanvas::predrawNotify() {
129 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000130 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000131 }
132}
133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000136/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 The clip/matrix/proc are fields that reflect the top of the save/restore
138 stack. Whenever the canvas changes, it marks a dirty flag, and then before
139 these are used (assuming we're not on a layer) we rebuild these cache
140 values: they reflect the top of the save stack, but translated and clipped
141 by the device's XY offset and bitmap-bounds.
142*/
143struct DeviceCM {
144 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000145 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000146 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000148 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000150 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 : fNext(NULL) {
152 if (NULL != device) {
153 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000154 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 }
reed@google.com4b226022011-01-11 18:32:13 +0000156 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000158 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000160 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000162 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 fDevice->unref();
164 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000165 SkDELETE(fPaint);
166 }
reed@google.com4b226022011-01-11 18:32:13 +0000167
reed@google.com045e62d2011-10-24 12:19:46 +0000168 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
169 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000170 int x = fDevice->getOrigin().x();
171 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 int width = fDevice->width();
173 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 if ((x | y) == 0) {
176 fMatrix = &totalMatrix;
177 fClip = totalClip;
178 } else {
179 fMatrixStorage = totalMatrix;
180 fMatrixStorage.postTranslate(SkIntToScalar(-x),
181 SkIntToScalar(-y));
182 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 totalClip.translate(-x, -y, &fClip);
185 }
186
reed@google.com045e62d2011-10-24 12:19:46 +0000187 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
189 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000190
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000192 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 SkRegion::kDifference_Op);
194 }
reed@google.com4b226022011-01-11 18:32:13 +0000195
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000196 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
197
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198#ifdef SK_DEBUG
199 if (!fClip.isEmpty()) {
200 SkIRect deviceR;
201 deviceR.set(0, 0, width, height);
202 SkASSERT(deviceR.contains(fClip.getBounds()));
203 }
204#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000205 }
206
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000208 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209};
210
211/* This is the record we keep for each save/restore level in the stack.
212 Since a level optionally copies the matrix and/or stack, we have pointers
213 for these fields. If the value is copied for this level, the copy is
214 stored in the ...Storage field, and the pointer points to that. If the
215 value is not copied for this level, we ignore ...Storage, and just point
216 at the corresponding value in the previous level in the stack.
217*/
218class SkCanvas::MCRec {
219public:
220 MCRec* fNext;
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000221 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000222 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
223 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
224 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 DeviceCM* fLayer;
227 /* If there are any layers in the stack, this points to the top-most
228 one that is at or below this level in the stack (so we know what
229 bitmap/device to draw into from this level. This value is NOT
230 reference counted, since the real owner is either our fLayer field,
231 or a previous one in a lower level.)
232 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000233 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000235 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 if (NULL != prev) {
237 if (flags & SkCanvas::kMatrix_SaveFlag) {
238 fMatrixStorage = *prev->fMatrix;
239 fMatrix = &fMatrixStorage;
240 } else {
241 fMatrix = prev->fMatrix;
242 }
reed@google.com4b226022011-01-11 18:32:13 +0000243
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000245 fRasterClipStorage = *prev->fRasterClip;
246 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 } else {
reed@google.com00177082011-10-12 14:34:30 +0000248 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 }
250
251 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000252 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
254 fTopLayer = prev->fTopLayer;
255 } else { // no prev
256 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000259 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 fFilter = NULL;
261 fTopLayer = NULL;
262 }
263 fLayer = NULL;
264
265 // don't bother initializing fNext
266 inc_rec();
267 }
268 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000269 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 SkDELETE(fLayer);
271 dec_rec();
272 }
reed@google.com4b226022011-01-11 18:32:13 +0000273
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274private:
reed@google.com00177082011-10-12 14:34:30 +0000275 SkMatrix fMatrixStorage;
276 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277};
278
279class SkDrawIter : public SkDraw {
280public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000281 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000282 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000283 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 canvas->updateDeviceCMCache();
285
reed@google.com90c07ea2012-04-13 13:50:27 +0000286 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 fBounder = canvas->getBounder();
288 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000289 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 }
reed@google.com4b226022011-01-11 18:32:13 +0000291
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 bool next() {
293 // skip over recs with empty clips
294 if (fSkipEmptyClips) {
295 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
296 fCurrLayer = fCurrLayer->fNext;
297 }
298 }
299
reed@google.comf68c5e22012-02-24 16:38:58 +0000300 const DeviceCM* rec = fCurrLayer;
301 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302
303 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000304 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
305 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 fDevice = rec->fDevice;
307 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000309 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310
311 fCurrLayer = rec->fNext;
312 if (fBounder) {
313 fBounder->setClip(fClip);
314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 return true;
318 }
319 return false;
320 }
reed@google.com4b226022011-01-11 18:32:13 +0000321
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000322 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323 int getX() const { return fDevice->getOrigin().x(); }
324 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 const SkMatrix& getMatrix() const { return *fMatrix; }
326 const SkRegion& getClip() const { return *fClip; }
327 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000328
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329private:
330 SkCanvas* fCanvas;
331 const DeviceCM* fCurrLayer;
332 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 SkBool8 fSkipEmptyClips;
334
335 typedef SkDraw INHERITED;
336};
337
338/////////////////////////////////////////////////////////////////////////////
339
340class AutoDrawLooper {
341public:
reed@google.com8926b162012-03-23 15:36:36 +0000342 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
343 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000344 fCanvas = canvas;
345 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000347 fPaint = NULL;
348 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000349 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000350 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351
reed@google.com8926b162012-03-23 15:36:36 +0000352 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
353 SkPaint tmp;
354 tmp.setImageFilter(fOrigPaint.getImageFilter());
355 // it would be nice if we had a guess at the bounds, instead of null
356 (void)canvas->internalSaveLayer(NULL, &tmp,
357 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
358 // we'll clear the imageFilter for the actual draws in next(), so
359 // it will only be applied during the restore().
360 fDoClearImageFilter = true;
361 }
362
reed@google.com4e2b3d32011-04-07 14:18:59 +0000363 if (fLooper) {
364 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000365 fIsSimple = false;
366 } else {
367 // can we be marked as simple?
368 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000369 }
370 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000371
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000373 if (fDoClearImageFilter) {
374 fCanvas->internalRestore();
375 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000378
reed@google.com4e2b3d32011-04-07 14:18:59 +0000379 const SkPaint& paint() const {
380 SkASSERT(fPaint);
381 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000383
reed@google.com129ec222012-05-15 13:24:09 +0000384 bool next(SkDrawFilter::Type drawType) {
385 if (fDone) {
386 return false;
387 } else if (fIsSimple) {
388 fDone = true;
389 fPaint = &fOrigPaint;
390 return !fPaint->nothingToDraw();
391 } else {
392 return this->doNext(drawType);
393 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000394 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000395
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000397 SkLazyPaint fLazyPaint;
398 SkCanvas* fCanvas;
399 const SkPaint& fOrigPaint;
400 SkDrawLooper* fLooper;
401 SkDrawFilter* fFilter;
402 const SkPaint* fPaint;
403 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000404 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000405 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000406 bool fIsSimple;
407
408 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409};
410
reed@google.com129ec222012-05-15 13:24:09 +0000411bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000412 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000413 SkASSERT(!fIsSimple);
414 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
415
416 SkPaint* paint = fLazyPaint.set(fOrigPaint);
417
418 if (fDoClearImageFilter) {
419 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000420 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421
reed@google.com129ec222012-05-15 13:24:09 +0000422 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000424 return false;
425 }
426 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000427 if (!fFilter->filter(paint, drawType)) {
428 fDone = true;
429 return false;
430 }
reed@google.com129ec222012-05-15 13:24:09 +0000431 if (NULL == fLooper) {
432 // no looper means we only draw once
433 fDone = true;
434 }
435 }
436 fPaint = paint;
437
438 // if we only came in here for the imagefilter, mark us as done
439 if (!fLooper && !fFilter) {
440 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000441 }
442
443 // call this after any possible paint modifiers
444 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000445 fPaint = NULL;
446 return false;
447 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 return true;
449}
450
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451/* Stack helper for managing a SkBounder. In the destructor, if we were
452 given a bounder, we call its commit() method, signifying that we are
453 done accumulating bounds for that draw.
454*/
455class SkAutoBounderCommit {
456public:
457 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
458 ~SkAutoBounderCommit() {
459 if (NULL != fBounder) {
460 fBounder->commit();
461 }
462 }
463private:
464 SkBounder* fBounder;
465};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000466#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467
468#include "SkColorPriv.h"
469
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470////////// macros to place around the internal draw calls //////////////////
471
reed@google.com8926b162012-03-23 15:36:36 +0000472#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000473 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000474 AutoDrawLooper looper(this, paint, true); \
475 while (looper.next(type)) { \
476 SkAutoBounderCommit ac(fBounder); \
477 SkDrawIter iter(this);
478
reed@google.com4e2b3d32011-04-07 14:18:59 +0000479#define LOOPER_BEGIN(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000480 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481 AutoDrawLooper looper(this, paint); \
482 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 SkAutoBounderCommit ac(fBounder); \
484 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000485
reed@google.com4e2b3d32011-04-07 14:18:59 +0000486#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487
488////////////////////////////////////////////////////////////////////////////
489
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000490SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000492 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000494 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000495 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000496 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000497 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000498 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 fMCRec = (MCRec*)fMCStack.push_back();
501 new (fMCRec) MCRec(NULL, 0);
502
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000503 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 fMCRec->fTopLayer = fMCRec->fLayer;
505 fMCRec->fNext = NULL;
506
reed@google.com97af1a62012-08-28 12:19:02 +0000507 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 return this->setDevice(device);
510}
511
reed@google.comcde92112011-07-06 20:00:52 +0000512SkCanvas::SkCanvas()
513: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000514 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000515
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000516 this->init(NULL);
517}
518
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000519SkCanvas::SkCanvas(SkBaseDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000520 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 inc_canvas();
522
523 this->init(device);
524}
525
526SkCanvas::SkCanvas(const SkBitmap& bitmap)
527 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
528 inc_canvas();
529
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000530 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531}
532
533SkCanvas::~SkCanvas() {
534 // free up the contents of our deque
535 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000536 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 this->internalRestore(); // restore the last, since we're going away
539
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000540 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000541 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000542
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 dec_canvas();
544}
545
546SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
547 SkRefCnt_SafeAssign(fBounder, bounder);
548 return bounder;
549}
550
551SkDrawFilter* SkCanvas::getDrawFilter() const {
552 return fMCRec->fFilter;
553}
554
555SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
556 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
557 return filter;
558}
559
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000560SkMetaData& SkCanvas::getMetaData() {
561 // metadata users are rare, so we lazily allocate it. If that changes we
562 // can decide to just make it a field in the device (rather than a ptr)
563 if (NULL == fMetaData) {
564 fMetaData = new SkMetaData;
565 }
566 return *fMetaData;
567}
568
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569///////////////////////////////////////////////////////////////////////////////
570
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000571void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000572 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000573 if (device) {
574 device->flush();
575 }
576}
577
reed@google.com210ce002011-11-01 14:24:23 +0000578SkISize SkCanvas::getDeviceSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000579 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000580 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
581}
582
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000583SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000585 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 SkASSERT(rec && rec->fLayer);
587 return rec->fLayer->fDevice;
588}
589
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000590SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000591 if (updateMatrixClip) {
592 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
593 }
reed@google.com9266fed2011-03-30 00:18:03 +0000594 return fMCRec->fTopLayer->fDevice;
595}
596
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000597SkBaseDevice* SkCanvas::setDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000599 SkDeque::F2BIter iter(fMCStack);
600 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000602 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603
604 if (rootDevice == device) {
605 return device;
606 }
reed@google.com4b226022011-01-11 18:32:13 +0000607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000609 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 }
611 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000612 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 }
614
615 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
616 rootDevice = device;
617
618 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 /* Now we update our initial region to have the bounds of the new device,
621 and then intersect all of the clips in our stack with these bounds,
622 to ensure that we can't draw outside of the device's bounds (and trash
623 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000624
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 NOTE: this is only a partial-fix, since if the new device is larger than
626 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000627 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
629 reconstruct the correct clips, so this approximation will have to do.
630 The caller really needs to restore() back to the base if they want to
631 accurately take advantage of the new device bounds.
632 */
633
reed@google.com42aea282012-03-28 16:19:15 +0000634 SkIRect bounds;
635 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000637 } else {
638 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 }
reed@google.com42aea282012-03-28 16:19:15 +0000640 // now jam our 1st clip to be bounds, and intersect the rest with that
641 rec->fRasterClip->setRect(bounds);
642 while ((rec = (MCRec*)iter.next()) != NULL) {
643 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
644 }
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 return device;
647}
648
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000649bool SkCanvas::readPixels(SkBitmap* bitmap,
650 int x, int y,
651 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000652 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000653 if (!device) {
654 return false;
655 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000656 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000657}
658
bsalomon@google.comc6980972011-11-02 19:57:21 +0000659bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000660 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000661 if (!device) {
662 return false;
663 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000664
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000665 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000666 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000667 if (!bounds.intersect(srcRect)) {
668 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000669 }
670
671 SkBitmap tmp;
672 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
673 bounds.height());
674 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
675 bitmap->swap(tmp);
676 return true;
677 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000678 return false;
679 }
reed@google.com51df9e32010-12-23 19:29:18 +0000680}
681
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000682void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
683 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000684 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000685 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000686 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
687 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
688 device->accessBitmap(true);
689 device->writePixels(bitmap, x, y, config8888);
690 }
reed@google.com51df9e32010-12-23 19:29:18 +0000691 }
692}
693
junov@google.com4370aed2012-01-18 16:21:08 +0000694SkCanvas* SkCanvas::canvasForDrawIter() {
695 return this;
696}
697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698//////////////////////////////////////////////////////////////////////////////
699
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700void SkCanvas::updateDeviceCMCache() {
701 if (fDeviceCMDirty) {
702 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000703 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000705
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000707 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000709 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000711 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 } while ((layer = layer->fNext) != NULL);
713 }
714 fDeviceCMDirty = false;
715 }
716}
717
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718///////////////////////////////////////////////////////////////////////////////
719
720int SkCanvas::internalSave(SaveFlags flags) {
721 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000722
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 MCRec* newTop = (MCRec*)fMCStack.push_back();
724 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 newTop->fNext = fMCRec;
727 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000728
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000729 if (SkCanvas::kClip_SaveFlag & flags) {
730 fClipStack.save();
731 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000732
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 return saveCount;
734}
735
736int SkCanvas::save(SaveFlags flags) {
737 // call shared impl
738 return this->internalSave(flags);
739}
740
741#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
742#define C16MASK (1 << SkBitmap::kRGB_565_Config)
743#define C8MASK (1 << SkBitmap::kA8_Config)
744
745static SkBitmap::Config resolve_config(SkCanvas* canvas,
746 const SkIRect& bounds,
747 SkCanvas::SaveFlags flags,
748 bool* isOpaque) {
749 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
750
751#if 0
752 // loop through and union all the configs we may draw into
753 uint32_t configMask = 0;
754 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
755 {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000756 SkBaseDevice* device = canvas->getLayerDevice(i);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 if (device->intersects(bounds))
758 configMask |= 1 << device->config();
759 }
760
761 // if the caller wants alpha or fullcolor, we can't return 565
762 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
763 SkCanvas::kHasAlphaLayer_SaveFlag))
764 configMask &= ~C16MASK;
765
766 switch (configMask) {
767 case C8MASK: // if we only have A8, return that
768 return SkBitmap::kA8_Config;
769
770 case C16MASK: // if we only have 565, return that
771 return SkBitmap::kRGB_565_Config;
772
773 default:
774 return SkBitmap::kARGB_8888_Config; // default answer
775 }
776#else
777 return SkBitmap::kARGB_8888_Config; // default answer
778#endif
779}
780
781static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
782 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
783}
784
junov@chromium.orga907ac32012-02-24 21:54:07 +0000785bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
786 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000787 SkIRect clipBounds;
788 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000789 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000790 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000791 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 if (NULL != bounds) {
793 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 this->getTotalMatrix().mapRect(&r, *bounds);
796 r.roundOut(&ir);
797 // early exit if the layer's bounds are clipped out
798 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000799 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000800 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000801 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000802 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 }
804 } else { // no user bounds, so just use the clip
805 ir = clipBounds;
806 }
807
reed@google.com5c3d1472011-02-22 19:12:23 +0000808 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 // early exit if the clip is now empty
811 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000812 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000813 return false;
814 }
815
816 if (intersection) {
817 *intersection = ir;
818 }
819 return true;
820}
821
822int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
823 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000824 return this->internalSaveLayer(bounds, paint, flags, false);
825}
826
827int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
828 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000829 // do this before we create the layer. We don't call the public save() since
830 // that would invoke a possibly overridden virtual
831 int count = this->internalSave(flags);
832
833 fDeviceCMDirty = true;
834
835 SkIRect ir;
836 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 return count;
838 }
839
reed@google.comb55deeb2012-01-06 14:43:09 +0000840 // Kill the imagefilter if our device doesn't allow it
841 SkLazyPaint lazyP;
842 if (paint && paint->getImageFilter()) {
843 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000844 if (justForImageFilter) {
845 // early exit if the layer was just for the imageFilter
846 return count;
847 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000848 SkPaint* p = lazyP.set(*paint);
849 p->setImageFilter(NULL);
850 paint = p;
851 }
852 }
853
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 bool isOpaque;
855 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
856
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000857 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000858 if (paint && paint->getImageFilter()) {
859 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
860 isOpaque);
861 } else {
862 device = this->createLayerDevice(config, ir.width(), ir.height(),
863 isOpaque);
864 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000865 if (NULL == device) {
866 SkDebugf("Unable to create device for layer.");
867 return count;
868 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000869
reed@google.com6f8f2922011-03-04 22:27:10 +0000870 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000871 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 device->unref();
873
874 layer->fNext = fMCRec->fTopLayer;
875 fMCRec->fLayer = layer;
876 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
877
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000878 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 return count;
880}
881
882int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
883 SaveFlags flags) {
884 if (0xFF == alpha) {
885 return this->saveLayer(bounds, NULL, flags);
886 } else {
887 SkPaint tmpPaint;
888 tmpPaint.setAlpha(alpha);
889 return this->saveLayer(bounds, &tmpPaint, flags);
890 }
891}
892
893void SkCanvas::restore() {
894 // check for underflow
895 if (fMCStack.count() > 1) {
896 this->internalRestore();
897 }
898}
899
900void SkCanvas::internalRestore() {
901 SkASSERT(fMCStack.count() != 0);
902
903 fDeviceCMDirty = true;
904 fLocalBoundsCompareTypeDirty = true;
905
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000906 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
907 fClipStack.restore();
908 }
909
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000910 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 DeviceCM* layer = fMCRec->fLayer; // may be null
912 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
913 fMCRec->fLayer = NULL;
914
915 // now do the normal restore()
916 fMCRec->~MCRec(); // balanced in save()
917 fMCStack.pop_back();
918 fMCRec = (MCRec*)fMCStack.back();
919
920 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
921 since if we're being recorded, we don't want to record this (the
922 recorder will have already recorded the restore).
923 */
924 if (NULL != layer) {
925 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000926 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000927 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
928 layer->fPaint);
929 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000931
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000932 SkASSERT(fSaveLayerCount > 0);
933 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 }
935 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000936 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937}
938
939int SkCanvas::getSaveCount() const {
940 return fMCStack.count();
941}
942
943void SkCanvas::restoreToCount(int count) {
944 // sanity check
945 if (count < 1) {
946 count = 1;
947 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000948
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000949 int n = this->getSaveCount() - count;
950 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 this->restore();
952 }
953}
954
reed@google.com7c202932011-12-14 18:48:05 +0000955bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000956 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000957}
958
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959/////////////////////////////////////////////////////////////////////////////
960
961// can't draw it if its empty, or its too big for a fixed-point width or height
962static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +0000963 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964}
965
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000966void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 const SkMatrix& matrix, const SkPaint* paint) {
968 if (reject_bitmap(bitmap)) {
969 return;
970 }
971
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000972 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000974 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000976
977 SkDEBUGCODE(bitmap.validate();)
978 CHECK_LOCKCOUNT_BALANCE(bitmap);
979
980 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
981
982 while (iter.next()) {
983 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
984 }
985
986 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987}
988
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000989void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +0000990 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 SkPaint tmp;
992 if (NULL == paint) {
993 tmp.setDither(true);
994 paint = &tmp;
995 }
reed@google.com4b226022011-01-11 18:32:13 +0000996
reed@google.com8926b162012-03-23 15:36:36 +0000997 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000999 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001000 paint = &looper.paint();
1001 SkImageFilter* filter = paint->getImageFilter();
1002 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001003 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001004 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001005 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +00001006 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001007 SkMatrix matrix = *iter.fMatrix;
1008 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1009 if (filter->filterImage(&proxy, src, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001010 SkPaint tmpUnfiltered(*paint);
1011 tmpUnfiltered.setImageFilter(NULL);
1012 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001013 }
1014 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001015 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001016 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001018 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019}
1020
reed@google.com8926b162012-03-23 15:36:36 +00001021void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1022 const SkPaint* paint) {
1023 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001024 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001025
reed@google.com8926b162012-03-23 15:36:36 +00001026 if (reject_bitmap(bitmap)) {
1027 return;
1028 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001029
reed@google.com8926b162012-03-23 15:36:36 +00001030 SkPaint tmp;
1031 if (NULL == paint) {
1032 paint = &tmp;
1033 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001034
reed@google.com8926b162012-03-23 15:36:36 +00001035 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001036
reed@google.com8926b162012-03-23 15:36:36 +00001037 while (iter.next()) {
1038 paint = &looper.paint();
1039 SkImageFilter* filter = paint->getImageFilter();
1040 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1041 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001042 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001043 SkBitmap dst;
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001044 SkMatrix matrix = *iter.fMatrix;
1045 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1046 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001047 SkPaint tmpUnfiltered(*paint);
1048 tmpUnfiltered.setImageFilter(NULL);
1049 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1050 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001051 }
1052 } else {
1053 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1054 }
1055 }
1056 LOOPER_END
1057}
1058
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059/////////////////////////////////////////////////////////////////////////////
1060
1061bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1062 fDeviceCMDirty = true;
1063 fLocalBoundsCompareTypeDirty = true;
1064 return fMCRec->fMatrix->preTranslate(dx, dy);
1065}
1066
1067bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1068 fDeviceCMDirty = true;
1069 fLocalBoundsCompareTypeDirty = true;
1070 return fMCRec->fMatrix->preScale(sx, sy);
1071}
1072
1073bool SkCanvas::rotate(SkScalar degrees) {
1074 fDeviceCMDirty = true;
1075 fLocalBoundsCompareTypeDirty = true;
1076 return fMCRec->fMatrix->preRotate(degrees);
1077}
1078
1079bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1080 fDeviceCMDirty = true;
1081 fLocalBoundsCompareTypeDirty = true;
1082 return fMCRec->fMatrix->preSkew(sx, sy);
1083}
1084
1085bool SkCanvas::concat(const SkMatrix& matrix) {
1086 fDeviceCMDirty = true;
1087 fLocalBoundsCompareTypeDirty = true;
1088 return fMCRec->fMatrix->preConcat(matrix);
1089}
1090
1091void SkCanvas::setMatrix(const SkMatrix& matrix) {
1092 fDeviceCMDirty = true;
1093 fLocalBoundsCompareTypeDirty = true;
1094 *fMCRec->fMatrix = matrix;
1095}
1096
1097// this is not virtual, so it must call a virtual method so that subclasses
1098// will see its action
1099void SkCanvas::resetMatrix() {
1100 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001101
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 matrix.reset();
1103 this->setMatrix(matrix);
1104}
1105
1106//////////////////////////////////////////////////////////////////////////////
1107
reed@google.comc42d35d2011-10-12 11:57:42 +00001108bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001109#ifdef SK_ENABLE_CLIP_QUICKREJECT
1110 if (SkRegion::kIntersect_Op == op) {
1111 if (fMCRec->fRasterClip->isEmpty()) {
1112 return false;
1113 }
1114
reed@google.com3b3e8952012-08-16 20:53:31 +00001115 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001116 fDeviceCMDirty = true;
1117 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001118
1119 fClipStack.clipEmpty();
1120 return fMCRec->fRasterClip->setEmpty();
1121 }
1122 }
1123#endif
1124
reed@google.com5c3d1472011-02-22 19:12:23 +00001125 AutoValidateClip avc(this);
1126
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 fDeviceCMDirty = true;
1128 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001129 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130
1131 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001132 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001133 // the matrix. This means we don't have to a) make a path, and b) tell
1134 // the region code to scan-convert the path, only to discover that it
1135 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137
1138 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001139 fClipStack.clipDevRect(r, op, doAA);
1140 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001142 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001143 // and clip against that, since it can handle any matrix. However, to
1144 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1145 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 SkPath path;
1147
1148 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001149 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 }
1151}
1152
reed@google.com00177082011-10-12 14:34:30 +00001153static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001154 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001155 // base is used to limit the size (and therefore memory allocation) of the
1156 // region that results from scan converting devPath.
1157 SkRegion base;
1158
reed@google.com819c9212011-02-23 18:56:55 +00001159 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001160 // since we are intersect, we can do better (tighter) with currRgn's
1161 // bounds, than just using the device. However, if currRgn is complex,
1162 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001163 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001164 // FIXME: we should also be able to do this when currClip->isBW(),
1165 // but relaxing the test above triggers GM asserts in
1166 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1167 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001168 } else {
reed@google.com00177082011-10-12 14:34:30 +00001169 base.setRect(currClip->getBounds());
1170 SkRasterClip clip;
1171 clip.setPath(devPath, base, doAA);
1172 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001173 }
reed@google.com819c9212011-02-23 18:56:55 +00001174 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001175 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001176 if (!device) {
1177 return currClip->setEmpty();
1178 }
1179
junov@chromium.orga907ac32012-02-24 21:54:07 +00001180 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001181
1182 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001183 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001184 } else {
reed@google.com00177082011-10-12 14:34:30 +00001185 SkRasterClip clip;
1186 clip.setPath(devPath, base, doAA);
1187 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001188 }
1189 }
1190}
1191
reed@google.com4ed0fb72012-12-12 20:48:18 +00001192bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1193 if (rrect.isRect()) {
1194 // call the non-virtual version
1195 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1196 } else {
1197 SkPath path;
1198 path.addRRect(rrect);
1199 // call the non-virtual version
1200 return this->SkCanvas::clipPath(path, op, doAA);
1201 }
1202}
1203
reed@google.comc42d35d2011-10-12 11:57:42 +00001204bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001205#ifdef SK_ENABLE_CLIP_QUICKREJECT
1206 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1207 if (fMCRec->fRasterClip->isEmpty()) {
1208 return false;
1209 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001210
reed@google.com3b3e8952012-08-16 20:53:31 +00001211 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001212 fDeviceCMDirty = true;
1213 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001214
reed@google.comda17f752012-08-16 18:27:05 +00001215 fClipStack.clipEmpty();
1216 return fMCRec->fRasterClip->setEmpty();
1217 }
1218 }
1219#endif
1220
reed@google.com5c3d1472011-02-22 19:12:23 +00001221 AutoValidateClip avc(this);
1222
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 fDeviceCMDirty = true;
1224 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001225 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226
1227 SkPath devPath;
1228 path.transform(*fMCRec->fMatrix, &devPath);
1229
reed@google.comfe701122011-11-08 19:41:23 +00001230 // Check if the transfomation, or the original path itself
1231 // made us empty. Note this can also happen if we contained NaN
1232 // values. computing the bounds detects this, and will set our
1233 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1234 if (devPath.getBounds().isEmpty()) {
1235 // resetting the path will remove any NaN or other wanky values
1236 // that might upset our scan converter.
1237 devPath.reset();
1238 }
1239
reed@google.com5c3d1472011-02-22 19:12:23 +00001240 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001241 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001242
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001243 if (fAllowSimplifyClip) {
1244 devPath.reset();
1245 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1246 const SkClipStack* clipStack = getClipStack();
1247 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1248 const SkClipStack::Element* element;
1249 while ((element = iter.next())) {
1250 SkClipStack::Element::Type type = element->getType();
1251 if (type == SkClipStack::Element::kEmpty_Type) {
1252 continue;
1253 }
1254 SkPath operand;
1255 if (type == SkClipStack::Element::kRect_Type) {
1256 operand.addRect(element->getRect());
1257 } else if (type == SkClipStack::Element::kPath_Type) {
1258 operand = element->getPath();
1259 } else {
1260 SkDEBUGFAIL("Unexpected type.");
1261 }
1262 SkRegion::Op elementOp = element->getOp();
1263 if (elementOp == SkRegion::kReplace_Op) {
1264 devPath = operand;
1265 } else {
1266 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1267 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001268 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1269 // perhaps we need an API change to avoid this sort of mixed-signals about
1270 // clipping.
1271 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001272 }
1273 op = SkRegion::kReplace_Op;
1274 }
1275
reed@google.com00177082011-10-12 14:34:30 +00001276 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277}
1278
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001279bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1280 bool inverseFilled) {
1281 // This is for updating the clip conservatively using only bounds
1282 // information.
1283 // Contract:
1284 // The current clip must contain the true clip. The true
1285 // clip is the clip that would have normally been computed
1286 // by calls to clipPath and clipRRect
1287 // Objective:
1288 // Keep the current clip as small as possible without
1289 // breaking the contract, using only clip bounding rectangles
1290 // (for performance).
1291
1292 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1293 // don't have to worry about getting caught in a loop. Thus anywhere
1294 // we call a virtual method, we explicitly prefix it with
1295 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001296
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001297 if (inverseFilled) {
1298 switch (op) {
1299 case SkRegion::kIntersect_Op:
1300 case SkRegion::kDifference_Op:
1301 // These ops can only shrink the current clip. So leaving
1302 // the clip unchanges conservatively respects the contract.
1303 return this->getClipDeviceBounds(NULL);
1304 case SkRegion::kUnion_Op:
1305 case SkRegion::kReplace_Op:
1306 case SkRegion::kReverseDifference_Op:
1307 case SkRegion::kXOR_Op:
1308 {
1309 // These ops can grow the current clip up to the extents of
1310 // the input clip, which is inverse filled, so we just set
1311 // the current clip to the device bounds.
1312 SkRect deviceBounds;
1313 SkIRect deviceIBounds;
1314 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001315 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001316 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1317 // set the clip in device space
1318 this->SkCanvas::setMatrix(SkMatrix::I());
1319 bool result = this->SkCanvas::clipRect(deviceBounds,
1320 SkRegion::kReplace_Op, false);
1321 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1322 return result;
1323 }
1324 default:
1325 SkASSERT(0); // unhandled op?
1326 }
1327 } else {
1328 // Not inverse filled
1329 switch (op) {
1330 case SkRegion::kIntersect_Op:
1331 case SkRegion::kUnion_Op:
1332 case SkRegion::kReplace_Op:
1333 return this->SkCanvas::clipRect(bounds, op, false);
1334 case SkRegion::kDifference_Op:
1335 // Difference can only shrink the current clip.
1336 // Leaving clip unchanged conservatively fullfills the contract.
1337 return this->getClipDeviceBounds(NULL);
1338 case SkRegion::kReverseDifference_Op:
1339 // To reverse, we swap in the bounds with a replace op.
1340 // As with difference, leave it unchanged.
1341 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1342 case SkRegion::kXOR_Op:
1343 // Be conservative, based on (A XOR B) always included in (A union B),
1344 // which is always included in (bounds(A) union bounds(B))
1345 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1346 default:
1347 SkASSERT(0); // unhandled op?
1348 }
1349 }
1350 return true;
1351}
1352
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001354 AutoValidateClip avc(this);
1355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 fDeviceCMDirty = true;
1357 fLocalBoundsCompareTypeDirty = true;
1358
reed@google.com5c3d1472011-02-22 19:12:23 +00001359 // todo: signal fClipStack that we have a region, and therefore (I guess)
1360 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001361 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001362
reed@google.com00177082011-10-12 14:34:30 +00001363 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364}
1365
reed@google.com819c9212011-02-23 18:56:55 +00001366#ifdef SK_DEBUG
1367void SkCanvas::validateClip() const {
1368 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001369 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001370 if (!device) {
1371 SkASSERT(this->getTotalClip().isEmpty());
1372 return;
1373 }
1374
reed@google.com819c9212011-02-23 18:56:55 +00001375 SkIRect ir;
1376 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001377 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001378
robertphillips@google.com80214e22012-07-20 15:33:18 +00001379 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001380 const SkClipStack::Element* element;
1381 while ((element = iter.next()) != NULL) {
1382 switch (element->getType()) {
1383 case SkClipStack::Element::kPath_Type:
1384 clipPathHelper(this,
1385 &tmpClip,
1386 element->getPath(),
1387 element->getOp(),
1388 element->isAA());
1389 break;
1390 case SkClipStack::Element::kRect_Type:
1391 element->getRect().round(&ir);
1392 tmpClip.op(ir, element->getOp());
1393 break;
1394 case SkClipStack::Element::kEmpty_Type:
1395 tmpClip.setEmpty();
1396 break;
reed@google.com819c9212011-02-23 18:56:55 +00001397 }
1398 }
1399
reed@google.com6f8f2922011-03-04 22:27:10 +00001400#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001401 // now compare against the current rgn
1402 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001403 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001404#endif
reed@google.com819c9212011-02-23 18:56:55 +00001405}
1406#endif
1407
reed@google.com90c07ea2012-04-13 13:50:27 +00001408void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001409 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001410 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001411
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001412 static const SkRect kEmpty = { 0, 0, 0, 0 };
1413 while ((element = iter.next()) != NULL) {
1414 switch (element->getType()) {
1415 case SkClipStack::Element::kPath_Type:
1416 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1417 break;
1418 case SkClipStack::Element::kRect_Type:
1419 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1420 break;
1421 case SkClipStack::Element::kEmpty_Type:
1422 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1423 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001424 }
1425 }
1426}
1427
reed@google.com5c3d1472011-02-22 19:12:23 +00001428///////////////////////////////////////////////////////////////////////////////
1429
reed@google.com3b3e8952012-08-16 20:53:31 +00001430void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001432
reed@google.com3b3e8952012-08-16 20:53:31 +00001433 if (!this->getClipBounds(&r)) {
1434 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001436 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1437 SkScalarToCompareType(r.fTop),
1438 SkScalarToCompareType(r.fRight),
1439 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 }
1441}
1442
reed@google.com3b3e8952012-08-16 20:53:31 +00001443bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001444
reed@google.com16078632011-12-06 18:56:37 +00001445 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001446 return true;
1447
reed@google.com00177082011-10-12 14:34:30 +00001448 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 return true;
1450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451
tomhudson@google.com8d430182011-06-06 19:11:19 +00001452 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001453 SkRect dst;
1454 fMCRec->fMatrix->mapRect(&dst, rect);
1455 SkIRect idst;
1456 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001457 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001458 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001459 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001460
reed@android.coma380ae42009-07-21 01:17:02 +00001461 // for speed, do the most likely reject compares first
1462 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1463 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1464 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1465 return true;
1466 }
1467 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1468 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1469 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1470 return true;
1471 }
1472 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
reed@google.com3b3e8952012-08-16 20:53:31 +00001476bool SkCanvas::quickReject(const SkPath& path) const {
1477 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001480static inline int pinIntForScalar(int x) {
1481#ifdef SK_SCALAR_IS_FIXED
1482 if (x < SK_MinS16) {
1483 x = SK_MinS16;
1484 } else if (x > SK_MaxS16) {
1485 x = SK_MaxS16;
1486 }
1487#endif
1488 return x;
1489}
1490
reed@google.com3b3e8952012-08-16 20:53:31 +00001491bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001492 SkIRect ibounds;
1493 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 return false;
1495 }
1496
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001497 SkMatrix inverse;
1498 // if we can't invert the CTM, we can't return local clip bounds
1499 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001500 if (bounds) {
1501 bounds->setEmpty();
1502 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001503 return false;
1504 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001506 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001507 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001508 // adjust it outwards in case we are antialiasing
1509 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001510
1511 // SkRect::iset() will correctly assert if we pass a value out of range
1512 // (when SkScalar==fixed), so we pin to legal values. This does not
1513 // really returnt the correct answer, but its the best we can do given
1514 // that we've promised to return SkRect (even though we support devices
1515 // that can be larger than 32K in width or height).
1516 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1517 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001518 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001519 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520 inverse.mapRect(bounds, r);
1521 }
1522 return true;
1523}
1524
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001525bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001526 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001527 if (clip.isEmpty()) {
1528 if (bounds) {
1529 bounds->setEmpty();
1530 }
1531 return false;
1532 }
1533
1534 if (NULL != bounds) {
1535 *bounds = clip.getBounds();
1536 }
1537 return true;
1538}
1539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540const SkMatrix& SkCanvas::getTotalMatrix() const {
1541 return *fMCRec->fMatrix;
1542}
1543
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001544SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001545 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1546 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001547 return kComplex_ClipType;
1548}
1549
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001551 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552}
1553
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001554SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001555 int width, int height,
1556 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001557 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001558 if (device) {
1559 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1560 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001561 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001562 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001563 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564}
1565
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001566SkBaseDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001567 int width, int height,
1568 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001569 SkBaseDevice* device = this->getDevice();
bsalomon@google.come97f0852011-06-17 13:10:25 +00001570 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001571 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001572 } else {
1573 return NULL;
1574 }
1575}
1576
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001577GrContext* SkCanvas::getGrContext() {
1578#if SK_SUPPORT_GPU
1579 SkBaseDevice* device = this->getTopDevice();
1580 if (NULL != device) {
1581 GrRenderTarget* renderTarget = device->accessRenderTarget();
1582 if (NULL != renderTarget) {
1583 return renderTarget->getContext();
1584 }
1585 }
1586#endif
1587
1588 return NULL;
1589
1590}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001591
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592//////////////////////////////////////////////////////////////////////////////
1593// These are the virtual drawing methods
1594//////////////////////////////////////////////////////////////////////////////
1595
reed@google.com2a981812011-04-14 18:59:28 +00001596void SkCanvas::clear(SkColor color) {
1597 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001598 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001599 while (iter.next()) {
1600 iter.fDevice->clear(color);
1601 }
1602}
1603
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001605 this->internalDrawPaint(paint);
1606}
1607
1608void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001609 CHECK_SHADER_NOSETCONTEXT(paint);
1610
reed@google.com4e2b3d32011-04-07 14:18:59 +00001611 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612
1613 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001614 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 }
1616
reed@google.com4e2b3d32011-04-07 14:18:59 +00001617 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618}
1619
1620void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1621 const SkPaint& paint) {
1622 if ((long)count <= 0) {
1623 return;
1624 }
1625
reed@google.comea033602012-12-14 13:13:55 +00001626 CHECK_SHADER_NOSETCONTEXT(paint);
1627
reed@google.coma584aed2012-05-16 14:06:02 +00001628 if (paint.canComputeFastBounds()) {
1629 SkRect r;
1630 // special-case 2 points (common for drawing a single line)
1631 if (2 == count) {
1632 r.set(pts[0], pts[1]);
1633 } else {
1634 r.set(pts, count);
1635 }
1636 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001637 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001638 return;
1639 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001640 }
reed@google.coma584aed2012-05-16 14:06:02 +00001641
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642 SkASSERT(pts != NULL);
1643
reed@google.com4e2b3d32011-04-07 14:18:59 +00001644 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001645
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001647 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648 }
reed@google.com4b226022011-01-11 18:32:13 +00001649
reed@google.com4e2b3d32011-04-07 14:18:59 +00001650 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651}
1652
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001653void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001654 CHECK_SHADER_NOSETCONTEXT(paint);
1655
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656 if (paint.canComputeFastBounds()) {
1657 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001658 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001659 return;
1660 }
1661 }
reed@google.com4b226022011-01-11 18:32:13 +00001662
reed@google.com4e2b3d32011-04-07 14:18:59 +00001663 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664
1665 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001666 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001667 }
1668
reed@google.com4e2b3d32011-04-07 14:18:59 +00001669 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670}
1671
reed@google.com4ed0fb72012-12-12 20:48:18 +00001672void SkCanvas::drawOval(const SkRect& oval, 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(oval, &storage))) {
1678 return;
1679 }
1680 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001681
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001682 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1683
1684 while (iter.next()) {
1685 iter.fDevice->drawOval(iter, oval, looper.paint());
1686 }
1687
1688 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001689}
1690
1691void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001692 CHECK_SHADER_NOSETCONTEXT(paint);
1693
reed@google.com4ed0fb72012-12-12 20:48:18 +00001694 if (paint.canComputeFastBounds()) {
1695 SkRect storage;
1696 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1697 return;
1698 }
1699 }
1700
1701 if (rrect.isRect()) {
1702 // call the non-virtual version
1703 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001704 return;
1705 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001706 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001707 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1708 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001709 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001710
1711 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1712
1713 while (iter.next()) {
1714 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1715 }
1716
1717 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001718}
1719
1720
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001721void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001722 CHECK_SHADER_NOSETCONTEXT(paint);
1723
reed@google.com93645112012-07-26 16:11:47 +00001724 if (!path.isFinite()) {
1725 return;
1726 }
1727
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001728 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001729 SkRect storage;
1730 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001731 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 return;
1733 }
1734 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001735 if (path.isEmpty()) {
1736 if (path.isInverseFillType()) {
1737 this->internalDrawPaint(paint);
1738 }
1739 return;
1740 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741
reed@google.com4e2b3d32011-04-07 14:18:59 +00001742 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743
1744 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001745 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746 }
1747
reed@google.com4e2b3d32011-04-07 14:18:59 +00001748 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749}
1750
1751void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1752 const SkPaint* paint) {
1753 SkDEBUGCODE(bitmap.validate();)
1754
reed@google.com3d608122011-11-21 15:16:16 +00001755 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001756 SkRect bounds = {
1757 x, y,
1758 x + SkIntToScalar(bitmap.width()),
1759 y + SkIntToScalar(bitmap.height())
1760 };
1761 if (paint) {
1762 (void)paint->computeFastBounds(bounds, &bounds);
1763 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001764 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765 return;
1766 }
1767 }
reed@google.com4b226022011-01-11 18:32:13 +00001768
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769 SkMatrix matrix;
1770 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001771 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772}
1773
reed@google.com9987ec32011-09-07 11:57:52 +00001774// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001775void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001776 const SkRect& dst, const SkPaint* paint,
1777 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1779 return;
1780 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001781
reed@google.comea033602012-12-14 13:13:55 +00001782 CHECK_LOCKCOUNT_BALANCE(bitmap);
1783
reed@google.com3d608122011-11-21 15:16:16 +00001784 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001785 SkRect storage;
1786 const SkRect* bounds = &dst;
1787 if (paint) {
1788 bounds = &paint->computeFastBounds(dst, &storage);
1789 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001790 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001791 return;
1792 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793 }
reed@google.com3d608122011-11-21 15:16:16 +00001794
reed@google.com33535f32012-09-25 15:37:50 +00001795 SkLazyPaint lazy;
1796 if (NULL == paint) {
1797 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001799
reed@google.com33535f32012-09-25 15:37:50 +00001800 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001801
reed@google.com33535f32012-09-25 15:37:50 +00001802 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001803 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001804 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001805
reed@google.com33535f32012-09-25 15:37:50 +00001806 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807}
1808
reed@google.com71121732012-09-18 15:14:33 +00001809void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001810 const SkRect& dst, const SkPaint* paint,
1811 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001812 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001813 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001814}
1815
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1817 const SkPaint* paint) {
1818 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001819 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001820}
1821
reed@google.com9987ec32011-09-07 11:57:52 +00001822void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1823 const SkIRect& center, const SkRect& dst,
1824 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001825 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001826 SkRect storage;
1827 const SkRect* bounds = &dst;
1828 if (paint) {
1829 bounds = &paint->computeFastBounds(dst, &storage);
1830 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001831 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001832 return;
1833 }
1834 }
1835
reed@google.com9987ec32011-09-07 11:57:52 +00001836 const int32_t w = bitmap.width();
1837 const int32_t h = bitmap.height();
1838
1839 SkIRect c = center;
1840 // pin center to the bounds of the bitmap
1841 c.fLeft = SkMax32(0, center.fLeft);
1842 c.fTop = SkMax32(0, center.fTop);
1843 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1844 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1845
reed@google.com71121732012-09-18 15:14:33 +00001846 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001847 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001848 };
1849 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001850 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001851 };
reed@google.com9987ec32011-09-07 11:57:52 +00001852 SkScalar dstX[4] = {
1853 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1854 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1855 };
1856 SkScalar dstY[4] = {
1857 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1858 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1859 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001860
reed@google.com9987ec32011-09-07 11:57:52 +00001861 if (dstX[1] > dstX[2]) {
1862 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1863 dstX[2] = dstX[1];
1864 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001865
reed@google.com9987ec32011-09-07 11:57:52 +00001866 if (dstY[1] > dstY[2]) {
1867 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1868 dstY[2] = dstY[1];
1869 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001870
reed@google.com9987ec32011-09-07 11:57:52 +00001871 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001872 SkRect s, d;
1873
reed@google.com9987ec32011-09-07 11:57:52 +00001874 s.fTop = srcY[y];
1875 s.fBottom = srcY[y+1];
1876 d.fTop = dstY[y];
1877 d.fBottom = dstY[y+1];
1878 for (int x = 0; x < 3; x++) {
1879 s.fLeft = srcX[x];
1880 s.fRight = srcX[x+1];
1881 d.fLeft = dstX[x];
1882 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001883 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001884 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001885 }
1886 }
1887}
1888
1889void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1890 const SkRect& dst, const SkPaint* paint) {
1891 SkDEBUGCODE(bitmap.validate();)
1892
1893 // Need a device entry-point, so gpu can use a mesh
1894 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1895}
1896
reed@google.comf67e4cf2011-03-15 20:56:58 +00001897class SkDeviceFilteredPaint {
1898public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001899 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1900 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001901 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001902 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001903 newPaint->setFlags(flags.fFlags);
1904 newPaint->setHinting(flags.fHinting);
1905 fPaint = newPaint;
1906 } else {
1907 fPaint = &paint;
1908 }
1909 }
1910
reed@google.comf67e4cf2011-03-15 20:56:58 +00001911 const SkPaint& paint() const { return *fPaint; }
1912
1913private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001914 const SkPaint* fPaint;
1915 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001916};
1917
bungeman@google.com52c748b2011-08-22 21:30:43 +00001918void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1919 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001920 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001921 draw.fDevice->drawRect(draw, r, paint);
1922 } else {
1923 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001924 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001925 draw.fDevice->drawRect(draw, r, p);
1926 }
1927}
1928
1929void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1930 const char text[], size_t byteLength,
1931 SkScalar x, SkScalar y) {
1932 SkASSERT(byteLength == 0 || text != NULL);
1933
1934 // nothing to draw
1935 if (text == NULL || byteLength == 0 ||
1936 draw.fClip->isEmpty() ||
1937 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1938 return;
1939 }
1940
1941 SkScalar width = 0;
1942 SkPoint start;
1943
1944 start.set(0, 0); // to avoid warning
1945 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1946 SkPaint::kStrikeThruText_Flag)) {
1947 width = paint.measureText(text, byteLength);
1948
1949 SkScalar offsetX = 0;
1950 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1951 offsetX = SkScalarHalf(width);
1952 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1953 offsetX = width;
1954 }
1955 start.set(x - offsetX, y);
1956 }
1957
1958 if (0 == width) {
1959 return;
1960 }
1961
1962 uint32_t flags = paint.getFlags();
1963
1964 if (flags & (SkPaint::kUnderlineText_Flag |
1965 SkPaint::kStrikeThruText_Flag)) {
1966 SkScalar textSize = paint.getTextSize();
1967 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1968 SkRect r;
1969
1970 r.fLeft = start.fX;
1971 r.fRight = start.fX + width;
1972
1973 if (flags & SkPaint::kUnderlineText_Flag) {
1974 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1975 start.fY);
1976 r.fTop = offset;
1977 r.fBottom = offset + height;
1978 DrawRect(draw, paint, r, textSize);
1979 }
1980 if (flags & SkPaint::kStrikeThruText_Flag) {
1981 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1982 start.fY);
1983 r.fTop = offset;
1984 r.fBottom = offset + height;
1985 DrawRect(draw, paint, r, textSize);
1986 }
1987 }
1988}
1989
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990void SkCanvas::drawText(const void* text, size_t byteLength,
1991 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001992 CHECK_SHADER_NOSETCONTEXT(paint);
1993
reed@google.com4e2b3d32011-04-07 14:18:59 +00001994 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995
1996 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001997 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001998 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001999 DrawTextDecorations(iter, dfp.paint(),
2000 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001 }
2002
reed@google.com4e2b3d32011-04-07 14:18:59 +00002003 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004}
2005
2006void SkCanvas::drawPosText(const void* text, size_t byteLength,
2007 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002008 CHECK_SHADER_NOSETCONTEXT(paint);
2009
reed@google.com4e2b3d32011-04-07 14:18:59 +00002010 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002011
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002013 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002015 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 }
reed@google.com4b226022011-01-11 18:32:13 +00002017
reed@google.com4e2b3d32011-04-07 14:18:59 +00002018 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019}
2020
2021void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2022 const SkScalar xpos[], SkScalar constY,
2023 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002024 CHECK_SHADER_NOSETCONTEXT(paint);
2025
reed@google.com4e2b3d32011-04-07 14:18:59 +00002026 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002027
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002029 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002031 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 }
reed@google.com4b226022011-01-11 18:32:13 +00002033
reed@google.com4e2b3d32011-04-07 14:18:59 +00002034 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035}
2036
2037void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2038 const SkPath& path, const SkMatrix* matrix,
2039 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002040 CHECK_SHADER_NOSETCONTEXT(paint);
2041
reed@google.com4e2b3d32011-04-07 14:18:59 +00002042 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043
2044 while (iter.next()) {
2045 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002046 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047 }
2048
reed@google.com4e2b3d32011-04-07 14:18:59 +00002049 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050}
2051
2052void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2053 const SkPoint verts[], const SkPoint texs[],
2054 const SkColor colors[], SkXfermode* xmode,
2055 const uint16_t indices[], int indexCount,
2056 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002057 CHECK_SHADER_NOSETCONTEXT(paint);
2058
reed@google.com4e2b3d32011-04-07 14:18:59 +00002059 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002060
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 while (iter.next()) {
2062 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002063 colors, xmode, indices, indexCount,
2064 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002065 }
reed@google.com4b226022011-01-11 18:32:13 +00002066
reed@google.com4e2b3d32011-04-07 14:18:59 +00002067 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068}
2069
2070//////////////////////////////////////////////////////////////////////////////
2071// These methods are NOT virtual, and therefore must call back into virtual
2072// methods, rather than actually drawing themselves.
2073//////////////////////////////////////////////////////////////////////////////
2074
2075void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002076 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077 SkPaint paint;
2078
2079 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002080 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002081 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082 }
2083 this->drawPaint(paint);
2084}
2085
reed@android.com845fdac2009-06-23 03:01:32 +00002086void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002087 SkPaint paint;
2088
2089 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002090 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002091 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 }
2093 this->drawPaint(paint);
2094}
2095
2096void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2097 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002098
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 pt.set(x, y);
2100 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2101}
2102
2103void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2104 SkPoint pt;
2105 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002106
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107 pt.set(x, y);
2108 paint.setColor(color);
2109 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2110}
2111
2112void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2113 const SkPaint& paint) {
2114 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002115
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 pts[0].set(x0, y0);
2117 pts[1].set(x1, y1);
2118 this->drawPoints(kLines_PointMode, 2, pts, paint);
2119}
2120
2121void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2122 SkScalar right, SkScalar bottom,
2123 const SkPaint& paint) {
2124 SkRect r;
2125
2126 r.set(left, top, right, bottom);
2127 this->drawRect(r, paint);
2128}
2129
2130void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2131 const SkPaint& paint) {
2132 if (radius < 0) {
2133 radius = 0;
2134 }
2135
2136 SkRect r;
2137 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002138 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139}
2140
2141void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2142 const SkPaint& paint) {
2143 if (rx > 0 && ry > 0) {
2144 if (paint.canComputeFastBounds()) {
2145 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002146 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 return;
2148 }
2149 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002150 SkRRect rrect;
2151 rrect.setRectXY(r, rx, ry);
2152 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153 } else {
2154 this->drawRect(r, paint);
2155 }
2156}
2157
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2159 SkScalar sweepAngle, bool useCenter,
2160 const SkPaint& paint) {
2161 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2162 this->drawOval(oval, paint);
2163 } else {
2164 SkPath path;
2165 if (useCenter) {
2166 path.moveTo(oval.centerX(), oval.centerY());
2167 }
2168 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2169 if (useCenter) {
2170 path.close();
2171 }
2172 this->drawPath(path, paint);
2173 }
2174}
2175
2176void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2177 const SkPath& path, SkScalar hOffset,
2178 SkScalar vOffset, const SkPaint& paint) {
2179 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002180
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 matrix.setTranslate(hOffset, vOffset);
2182 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2183}
2184
reed@android.comf76bacf2009-05-13 14:00:33 +00002185///////////////////////////////////////////////////////////////////////////////
2186
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189}
2190
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191///////////////////////////////////////////////////////////////////////////////
2192///////////////////////////////////////////////////////////////////////////////
2193
2194SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002195 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196
2197 SkASSERT(canvas);
2198
2199 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2200 fDone = !fImpl->next();
2201}
2202
2203SkCanvas::LayerIter::~LayerIter() {
2204 fImpl->~SkDrawIter();
2205}
2206
2207void SkCanvas::LayerIter::next() {
2208 fDone = !fImpl->next();
2209}
2210
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002211SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002212 return fImpl->getDevice();
2213}
2214
2215const SkMatrix& SkCanvas::LayerIter::matrix() const {
2216 return fImpl->getMatrix();
2217}
2218
2219const SkPaint& SkCanvas::LayerIter::paint() const {
2220 const SkPaint* paint = fImpl->getPaint();
2221 if (NULL == paint) {
2222 paint = &fDefaultPaint;
2223 }
2224 return *paint;
2225}
2226
2227const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2228int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2229int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002230
2231///////////////////////////////////////////////////////////////////////////////
2232
2233SkCanvas::ClipVisitor::~ClipVisitor() { }