blob: d143b85a53826f3d3496143f3f5531f6d9916950 [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"
11#include "SkBounder.h"
12#include "SkDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
reed@google.com82ce2b82012-06-26 17:43:26 +000029SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000030SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000031SK_DEFINE_INST_COUNT(SkDrawFilter)
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
94class AutoCheckNoSetContext {
95public:
96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
97 this->assertNoSetContext(fPaint);
98 }
99 ~AutoCheckNoSetContext() {
100 this->assertNoSetContext(fPaint);
101 }
102
103private:
104 const SkPaint& fPaint;
105
106 void assertNoSetContext(const SkPaint& paint) {
107 SkShader* s = paint.getShader();
108 if (s) {
109 SkASSERT(!s->setContextHasBeenCalled());
110 }
111 }
112};
113
114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
119 #define CHECK_SHADER_NOSETCONTEXT(paint)
120#endif
121
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000122typedef SkTLazy<SkPaint> SkLazyPaint;
123
reed@google.com97af1a62012-08-28 12:19:02 +0000124void SkCanvas::predrawNotify() {
125 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000127 }
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
132/* This is the record we keep for each SkDevice that the user installs.
133 The clip/matrix/proc are fields that reflect the top of the save/restore
134 stack. Whenever the canvas changes, it marks a dirty flag, and then before
135 these are used (assuming we're not on a layer) we rebuild these cache
136 values: they reflect the top of the save stack, but translated and clipped
137 by the device's XY offset and bitmap-bounds.
138*/
139struct DeviceCM {
140 DeviceCM* fNext;
141 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000142 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000146 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 : fNext(NULL) {
148 if (NULL != device) {
149 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000150 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@google.com4b226022011-01-11 18:32:13 +0000152 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000156 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000158 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fDevice->unref();
160 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000161 SkDELETE(fPaint);
162 }
reed@google.com4b226022011-01-11 18:32:13 +0000163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
165 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000166 int x = fDevice->getOrigin().x();
167 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 int width = fDevice->width();
169 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if ((x | y) == 0) {
172 fMatrix = &totalMatrix;
173 fClip = totalClip;
174 } else {
175 fMatrixStorage = totalMatrix;
176 fMatrixStorage.postTranslate(SkIntToScalar(-x),
177 SkIntToScalar(-y));
178 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 totalClip.translate(-x, -y, &fClip);
181 }
182
reed@google.com045e62d2011-10-24 12:19:46 +0000183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000186
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkRegion::kDifference_Op);
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#ifdef SK_DEBUG
195 if (!fClip.isEmpty()) {
196 SkIRect deviceR;
197 deviceR.set(0, 0, width, height);
198 SkASSERT(deviceR.contains(fClip.getBounds()));
199 }
200#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000201 }
202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000204 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207/* This is the record we keep for each save/restore level in the stack.
208 Since a level optionally copies the matrix and/or stack, we have pointers
209 for these fields. If the value is copied for this level, the copy is
210 stored in the ...Storage field, and the pointer points to that. If the
211 value is not copied for this level, we ignore ...Storage, and just point
212 at the corresponding value in the previous level in the stack.
213*/
214class SkCanvas::MCRec {
215public:
216 MCRec* fNext;
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000217 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000218 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
219 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
220 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 DeviceCM* fLayer;
223 /* If there are any layers in the stack, this points to the top-most
224 one that is at or below this level in the stack (so we know what
225 bitmap/device to draw into from this level. This value is NOT
226 reference counted, since the real owner is either our fLayer field,
227 or a previous one in a lower level.)
228 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000229 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000231 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 if (NULL != prev) {
233 if (flags & SkCanvas::kMatrix_SaveFlag) {
234 fMatrixStorage = *prev->fMatrix;
235 fMatrix = &fMatrixStorage;
236 } else {
237 fMatrix = prev->fMatrix;
238 }
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000241 fRasterClipStorage = *prev->fRasterClip;
242 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 } else {
reed@google.com00177082011-10-12 14:34:30 +0000244 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 }
246
247 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000248 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249
250 fTopLayer = prev->fTopLayer;
251 } else { // no prev
252 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000255 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 fFilter = NULL;
257 fTopLayer = NULL;
258 }
259 fLayer = NULL;
260
261 // don't bother initializing fNext
262 inc_rec();
263 }
264 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000265 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkDELETE(fLayer);
267 dec_rec();
268 }
reed@google.com4b226022011-01-11 18:32:13 +0000269
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270private:
reed@google.com00177082011-10-12 14:34:30 +0000271 SkMatrix fMatrixStorage;
272 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
275class SkDrawIter : public SkDraw {
276public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000277 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000278 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000279 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 canvas->updateDeviceCMCache();
281
reed@google.com90c07ea2012-04-13 13:50:27 +0000282 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fBounder = canvas->getBounder();
284 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000285 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 }
reed@google.com4b226022011-01-11 18:32:13 +0000287
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 bool next() {
289 // skip over recs with empty clips
290 if (fSkipEmptyClips) {
291 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
292 fCurrLayer = fCurrLayer->fNext;
293 }
294 }
295
reed@google.comf68c5e22012-02-24 16:38:58 +0000296 const DeviceCM* rec = fCurrLayer;
297 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298
299 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000300 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
301 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 fDevice = rec->fDevice;
303 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000305 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306
307 fCurrLayer = rec->fNext;
308 if (fBounder) {
309 fBounder->setClip(fClip);
310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000312
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 return true;
314 }
315 return false;
316 }
reed@google.com4b226022011-01-11 18:32:13 +0000317
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000319 int getX() const { return fDevice->getOrigin().x(); }
320 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 const SkMatrix& getMatrix() const { return *fMatrix; }
322 const SkRegion& getClip() const { return *fClip; }
323 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000324
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325private:
326 SkCanvas* fCanvas;
327 const DeviceCM* fCurrLayer;
328 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 SkBool8 fSkipEmptyClips;
330
331 typedef SkDraw INHERITED;
332};
333
334/////////////////////////////////////////////////////////////////////////////
335
336class AutoDrawLooper {
337public:
reed@google.com8926b162012-03-23 15:36:36 +0000338 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
339 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fCanvas = canvas;
341 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000343 fPaint = NULL;
344 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000345 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000346 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
reed@google.com8926b162012-03-23 15:36:36 +0000348 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
349 SkPaint tmp;
350 tmp.setImageFilter(fOrigPaint.getImageFilter());
351 // it would be nice if we had a guess at the bounds, instead of null
352 (void)canvas->internalSaveLayer(NULL, &tmp,
353 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
354 // we'll clear the imageFilter for the actual draws in next(), so
355 // it will only be applied during the restore().
356 fDoClearImageFilter = true;
357 }
358
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359 if (fLooper) {
360 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000361 fIsSimple = false;
362 } else {
363 // can we be marked as simple?
364 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000365 }
366 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000367
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000369 if (fDoClearImageFilter) {
370 fCanvas->internalRestore();
371 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000372 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000374
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 const SkPaint& paint() const {
376 SkASSERT(fPaint);
377 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000379
reed@google.com129ec222012-05-15 13:24:09 +0000380 bool next(SkDrawFilter::Type drawType) {
381 if (fDone) {
382 return false;
383 } else if (fIsSimple) {
384 fDone = true;
385 fPaint = &fOrigPaint;
386 return !fPaint->nothingToDraw();
387 } else {
388 return this->doNext(drawType);
389 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000390 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000391
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000393 SkLazyPaint fLazyPaint;
394 SkCanvas* fCanvas;
395 const SkPaint& fOrigPaint;
396 SkDrawLooper* fLooper;
397 SkDrawFilter* fFilter;
398 const SkPaint* fPaint;
399 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000400 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000401 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000402 bool fIsSimple;
403
404 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405};
406
reed@google.com129ec222012-05-15 13:24:09 +0000407bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000408 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000409 SkASSERT(!fIsSimple);
410 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
411
412 SkPaint* paint = fLazyPaint.set(fOrigPaint);
413
414 if (fDoClearImageFilter) {
415 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000417
reed@google.com129ec222012-05-15 13:24:09 +0000418 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000420 return false;
421 }
422 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000423 if (!fFilter->filter(paint, drawType)) {
424 fDone = true;
425 return false;
426 }
reed@google.com129ec222012-05-15 13:24:09 +0000427 if (NULL == fLooper) {
428 // no looper means we only draw once
429 fDone = true;
430 }
431 }
432 fPaint = paint;
433
434 // if we only came in here for the imagefilter, mark us as done
435 if (!fLooper && !fFilter) {
436 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000437 }
438
439 // call this after any possible paint modifiers
440 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000441 fPaint = NULL;
442 return false;
443 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000444 return true;
445}
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447/* Stack helper for managing a SkBounder. In the destructor, if we were
448 given a bounder, we call its commit() method, signifying that we are
449 done accumulating bounds for that draw.
450*/
451class SkAutoBounderCommit {
452public:
453 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
454 ~SkAutoBounderCommit() {
455 if (NULL != fBounder) {
456 fBounder->commit();
457 }
458 }
459private:
460 SkBounder* fBounder;
461};
462
463#include "SkColorPriv.h"
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465////////// macros to place around the internal draw calls //////////////////
466
reed@google.com8926b162012-03-23 15:36:36 +0000467#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000468 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000469 AutoDrawLooper looper(this, paint, true); \
470 while (looper.next(type)) { \
471 SkAutoBounderCommit ac(fBounder); \
472 SkDrawIter iter(this);
473
reed@google.com4e2b3d32011-04-07 14:18:59 +0000474#define LOOPER_BEGIN(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000475 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000476 AutoDrawLooper looper(this, paint); \
477 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkAutoBounderCommit ac(fBounder); \
479 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000480
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482
483////////////////////////////////////////////////////////////////////////////
484
485SkDevice* SkCanvas::init(SkDevice* device) {
486 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000487 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000489 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000490 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000491 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000492 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000493 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494
495 fMCRec = (MCRec*)fMCStack.push_back();
496 new (fMCRec) MCRec(NULL, 0);
497
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000498 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 fMCRec->fTopLayer = fMCRec->fLayer;
500 fMCRec->fNext = NULL;
501
reed@google.com97af1a62012-08-28 12:19:02 +0000502 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 return this->setDevice(device);
505}
506
reed@google.comcde92112011-07-06 20:00:52 +0000507SkCanvas::SkCanvas()
508: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000509 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000510
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000511 this->init(NULL);
512}
513
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000515 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 inc_canvas();
517
518 this->init(device);
519}
520
521SkCanvas::SkCanvas(const SkBitmap& bitmap)
522 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
523 inc_canvas();
524
reed@google.comcde92112011-07-06 20:00:52 +0000525 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526}
527
528SkCanvas::~SkCanvas() {
529 // free up the contents of our deque
530 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000531 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 this->internalRestore(); // restore the last, since we're going away
534
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000535 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000536 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 dec_canvas();
539}
540
541SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
542 SkRefCnt_SafeAssign(fBounder, bounder);
543 return bounder;
544}
545
546SkDrawFilter* SkCanvas::getDrawFilter() const {
547 return fMCRec->fFilter;
548}
549
550SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
551 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
552 return filter;
553}
554
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000555SkMetaData& SkCanvas::getMetaData() {
556 // metadata users are rare, so we lazily allocate it. If that changes we
557 // can decide to just make it a field in the device (rather than a ptr)
558 if (NULL == fMetaData) {
559 fMetaData = new SkMetaData;
560 }
561 return *fMetaData;
562}
563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564///////////////////////////////////////////////////////////////////////////////
565
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000566void SkCanvas::flush() {
567 SkDevice* device = this->getDevice();
568 if (device) {
569 device->flush();
570 }
571}
572
reed@google.com210ce002011-11-01 14:24:23 +0000573SkISize SkCanvas::getDeviceSize() const {
574 SkDevice* d = this->getDevice();
575 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
576}
577
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578SkDevice* SkCanvas::getDevice() const {
579 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000580 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 SkASSERT(rec && rec->fLayer);
582 return rec->fLayer->fDevice;
583}
584
reed@google.com0b53d592012-03-19 18:26:34 +0000585SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
586 if (updateMatrixClip) {
587 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
588 }
reed@google.com9266fed2011-03-30 00:18:03 +0000589 return fMCRec->fTopLayer->fDevice;
590}
591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592SkDevice* SkCanvas::setDevice(SkDevice* device) {
593 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000594 SkDeque::F2BIter iter(fMCStack);
595 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 SkASSERT(rec && rec->fLayer);
597 SkDevice* rootDevice = rec->fLayer->fDevice;
598
599 if (rootDevice == device) {
600 return device;
601 }
reed@google.com4b226022011-01-11 18:32:13 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000604 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 }
606 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000607 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 }
609
610 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
611 rootDevice = device;
612
613 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 /* Now we update our initial region to have the bounds of the new device,
616 and then intersect all of the clips in our stack with these bounds,
617 to ensure that we can't draw outside of the device's bounds (and trash
618 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 NOTE: this is only a partial-fix, since if the new device is larger than
621 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000622 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
624 reconstruct the correct clips, so this approximation will have to do.
625 The caller really needs to restore() back to the base if they want to
626 accurately take advantage of the new device bounds.
627 */
628
reed@google.com42aea282012-03-28 16:19:15 +0000629 SkIRect bounds;
630 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000632 } else {
633 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
reed@google.com42aea282012-03-28 16:19:15 +0000635 // now jam our 1st clip to be bounds, and intersect the rest with that
636 rec->fRasterClip->setRect(bounds);
637 while ((rec = (MCRec*)iter.next()) != NULL) {
638 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
639 }
640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 return device;
642}
643
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000644bool SkCanvas::readPixels(SkBitmap* bitmap,
645 int x, int y,
646 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000647 SkDevice* device = this->getDevice();
648 if (!device) {
649 return false;
650 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000651 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000652}
653
bsalomon@google.comc6980972011-11-02 19:57:21 +0000654bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000655 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000656 if (!device) {
657 return false;
658 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000659
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000660 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000661 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000662 if (!bounds.intersect(srcRect)) {
663 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000664 }
665
666 SkBitmap tmp;
667 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
668 bounds.height());
669 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
670 bitmap->swap(tmp);
671 return true;
672 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000673 return false;
674 }
reed@google.com51df9e32010-12-23 19:29:18 +0000675}
676
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000677void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
678 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000679 SkDevice* device = this->getDevice();
680 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000681 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
682 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
683 device->accessBitmap(true);
684 device->writePixels(bitmap, x, y, config8888);
685 }
reed@google.com51df9e32010-12-23 19:29:18 +0000686 }
687}
688
junov@google.com4370aed2012-01-18 16:21:08 +0000689SkCanvas* SkCanvas::canvasForDrawIter() {
690 return this;
691}
692
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693//////////////////////////////////////////////////////////////////////////////
694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695void SkCanvas::updateDeviceCMCache() {
696 if (fDeviceCMDirty) {
697 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000698 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000700
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000702 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000704 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000706 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 } while ((layer = layer->fNext) != NULL);
708 }
709 fDeviceCMDirty = false;
710 }
711}
712
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713///////////////////////////////////////////////////////////////////////////////
714
715int SkCanvas::internalSave(SaveFlags flags) {
716 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000717
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 MCRec* newTop = (MCRec*)fMCStack.push_back();
719 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000720
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 newTop->fNext = fMCRec;
722 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000723
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000724 if (SkCanvas::kClip_SaveFlag & flags) {
725 fClipStack.save();
726 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 return saveCount;
729}
730
731int SkCanvas::save(SaveFlags flags) {
732 // call shared impl
733 return this->internalSave(flags);
734}
735
736#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
737#define C16MASK (1 << SkBitmap::kRGB_565_Config)
738#define C8MASK (1 << SkBitmap::kA8_Config)
739
740static SkBitmap::Config resolve_config(SkCanvas* canvas,
741 const SkIRect& bounds,
742 SkCanvas::SaveFlags flags,
743 bool* isOpaque) {
744 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
745
746#if 0
747 // loop through and union all the configs we may draw into
748 uint32_t configMask = 0;
749 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
750 {
751 SkDevice* device = canvas->getLayerDevice(i);
752 if (device->intersects(bounds))
753 configMask |= 1 << device->config();
754 }
755
756 // if the caller wants alpha or fullcolor, we can't return 565
757 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
758 SkCanvas::kHasAlphaLayer_SaveFlag))
759 configMask &= ~C16MASK;
760
761 switch (configMask) {
762 case C8MASK: // if we only have A8, return that
763 return SkBitmap::kA8_Config;
764
765 case C16MASK: // if we only have 565, return that
766 return SkBitmap::kRGB_565_Config;
767
768 default:
769 return SkBitmap::kARGB_8888_Config; // default answer
770 }
771#else
772 return SkBitmap::kARGB_8888_Config; // default answer
773#endif
774}
775
776static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
777 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
778}
779
junov@chromium.orga907ac32012-02-24 21:54:07 +0000780bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
781 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000782 SkIRect clipBounds;
783 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000784 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000785 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000786 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 if (NULL != bounds) {
788 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 this->getTotalMatrix().mapRect(&r, *bounds);
791 r.roundOut(&ir);
792 // early exit if the layer's bounds are clipped out
793 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000794 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000795 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000796 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000797 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 }
799 } else { // no user bounds, so just use the clip
800 ir = clipBounds;
801 }
802
reed@google.com5c3d1472011-02-22 19:12:23 +0000803 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000804
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 // early exit if the clip is now empty
806 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000807 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000808 return false;
809 }
810
811 if (intersection) {
812 *intersection = ir;
813 }
814 return true;
815}
816
817int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
818 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000819 return this->internalSaveLayer(bounds, paint, flags, false);
820}
821
822int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
823 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000824 // do this before we create the layer. We don't call the public save() since
825 // that would invoke a possibly overridden virtual
826 int count = this->internalSave(flags);
827
828 fDeviceCMDirty = true;
829
830 SkIRect ir;
831 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return count;
833 }
834
reed@google.comb55deeb2012-01-06 14:43:09 +0000835 // Kill the imagefilter if our device doesn't allow it
836 SkLazyPaint lazyP;
837 if (paint && paint->getImageFilter()) {
838 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000839 if (justForImageFilter) {
840 // early exit if the layer was just for the imageFilter
841 return count;
842 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000843 SkPaint* p = lazyP.set(*paint);
844 p->setImageFilter(NULL);
845 paint = p;
846 }
847 }
848
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 bool isOpaque;
850 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
851
reed@google.com76dd2772012-01-05 21:15:07 +0000852 SkDevice* device;
853 if (paint && paint->getImageFilter()) {
854 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
855 isOpaque);
856 } else {
857 device = this->createLayerDevice(config, ir.width(), ir.height(),
858 isOpaque);
859 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000860 if (NULL == device) {
861 SkDebugf("Unable to create device for layer.");
862 return count;
863 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000864
reed@google.com6f8f2922011-03-04 22:27:10 +0000865 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000866 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 device->unref();
868
869 layer->fNext = fMCRec->fTopLayer;
870 fMCRec->fLayer = layer;
871 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
872
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000873 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 return count;
875}
876
877int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
878 SaveFlags flags) {
879 if (0xFF == alpha) {
880 return this->saveLayer(bounds, NULL, flags);
881 } else {
882 SkPaint tmpPaint;
883 tmpPaint.setAlpha(alpha);
884 return this->saveLayer(bounds, &tmpPaint, flags);
885 }
886}
887
888void SkCanvas::restore() {
889 // check for underflow
890 if (fMCStack.count() > 1) {
891 this->internalRestore();
892 }
893}
894
895void SkCanvas::internalRestore() {
896 SkASSERT(fMCStack.count() != 0);
897
898 fDeviceCMDirty = true;
899 fLocalBoundsCompareTypeDirty = true;
900
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000901 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
902 fClipStack.restore();
903 }
904
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000905 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 DeviceCM* layer = fMCRec->fLayer; // may be null
907 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
908 fMCRec->fLayer = NULL;
909
910 // now do the normal restore()
911 fMCRec->~MCRec(); // balanced in save()
912 fMCStack.pop_back();
913 fMCRec = (MCRec*)fMCStack.back();
914
915 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
916 since if we're being recorded, we don't want to record this (the
917 recorder will have already recorded the restore).
918 */
919 if (NULL != layer) {
920 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000921 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000922 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
923 layer->fPaint);
924 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000926
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000927 SkASSERT(fSaveLayerCount > 0);
928 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 }
930 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000931 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932}
933
934int SkCanvas::getSaveCount() const {
935 return fMCStack.count();
936}
937
938void SkCanvas::restoreToCount(int count) {
939 // sanity check
940 if (count < 1) {
941 count = 1;
942 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000943
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000944 int n = this->getSaveCount() - count;
945 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 this->restore();
947 }
948}
949
reed@google.com7c202932011-12-14 18:48:05 +0000950bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000951 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000952}
953
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954/////////////////////////////////////////////////////////////////////////////
955
956// can't draw it if its empty, or its too big for a fixed-point width or height
957static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +0000958 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959}
960
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000961void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 const SkMatrix& matrix, const SkPaint* paint) {
963 if (reject_bitmap(bitmap)) {
964 return;
965 }
966
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000967 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000969 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000971
972 SkDEBUGCODE(bitmap.validate();)
973 CHECK_LOCKCOUNT_BALANCE(bitmap);
974
975 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
976
977 while (iter.next()) {
978 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
979 }
980
981 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982}
983
reed@google.com8926b162012-03-23 15:36:36 +0000984void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
985 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 SkPaint tmp;
987 if (NULL == paint) {
988 tmp.setDither(true);
989 paint = &tmp;
990 }
reed@google.com4b226022011-01-11 18:32:13 +0000991
reed@google.com8926b162012-03-23 15:36:36 +0000992 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000994 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000995 paint = &looper.paint();
996 SkImageFilter* filter = paint->getImageFilter();
997 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000998 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000999 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001000 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +00001001 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +00001002 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001003 SkPaint tmpUnfiltered(*paint);
1004 tmpUnfiltered.setImageFilter(NULL);
1005 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001006 }
1007 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001008 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001009 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001011 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012}
1013
reed@google.com8926b162012-03-23 15:36:36 +00001014void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1015 const SkPaint* paint) {
1016 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001017 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001018
reed@google.com8926b162012-03-23 15:36:36 +00001019 if (reject_bitmap(bitmap)) {
1020 return;
1021 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001022
reed@google.com8926b162012-03-23 15:36:36 +00001023 SkPaint tmp;
1024 if (NULL == paint) {
1025 paint = &tmp;
1026 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001027
reed@google.com8926b162012-03-23 15:36:36 +00001028 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001029
reed@google.com8926b162012-03-23 15:36:36 +00001030 while (iter.next()) {
1031 paint = &looper.paint();
1032 SkImageFilter* filter = paint->getImageFilter();
1033 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1034 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001035 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001036 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001037 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1038 &dst, &pos)) {
1039 SkPaint tmpUnfiltered(*paint);
1040 tmpUnfiltered.setImageFilter(NULL);
1041 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1042 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001043 }
1044 } else {
1045 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1046 }
1047 }
1048 LOOPER_END
1049}
1050
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051/////////////////////////////////////////////////////////////////////////////
1052
1053bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1054 fDeviceCMDirty = true;
1055 fLocalBoundsCompareTypeDirty = true;
1056 return fMCRec->fMatrix->preTranslate(dx, dy);
1057}
1058
1059bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1060 fDeviceCMDirty = true;
1061 fLocalBoundsCompareTypeDirty = true;
1062 return fMCRec->fMatrix->preScale(sx, sy);
1063}
1064
1065bool SkCanvas::rotate(SkScalar degrees) {
1066 fDeviceCMDirty = true;
1067 fLocalBoundsCompareTypeDirty = true;
1068 return fMCRec->fMatrix->preRotate(degrees);
1069}
1070
1071bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1072 fDeviceCMDirty = true;
1073 fLocalBoundsCompareTypeDirty = true;
1074 return fMCRec->fMatrix->preSkew(sx, sy);
1075}
1076
1077bool SkCanvas::concat(const SkMatrix& matrix) {
1078 fDeviceCMDirty = true;
1079 fLocalBoundsCompareTypeDirty = true;
1080 return fMCRec->fMatrix->preConcat(matrix);
1081}
1082
1083void SkCanvas::setMatrix(const SkMatrix& matrix) {
1084 fDeviceCMDirty = true;
1085 fLocalBoundsCompareTypeDirty = true;
1086 *fMCRec->fMatrix = matrix;
1087}
1088
1089// this is not virtual, so it must call a virtual method so that subclasses
1090// will see its action
1091void SkCanvas::resetMatrix() {
1092 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001093
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 matrix.reset();
1095 this->setMatrix(matrix);
1096}
1097
1098//////////////////////////////////////////////////////////////////////////////
1099
reed@google.comc42d35d2011-10-12 11:57:42 +00001100bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001101#ifdef SK_ENABLE_CLIP_QUICKREJECT
1102 if (SkRegion::kIntersect_Op == op) {
1103 if (fMCRec->fRasterClip->isEmpty()) {
1104 return false;
1105 }
1106
reed@google.com3b3e8952012-08-16 20:53:31 +00001107 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001108 fDeviceCMDirty = true;
1109 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001110
1111 fClipStack.clipEmpty();
1112 return fMCRec->fRasterClip->setEmpty();
1113 }
1114 }
1115#endif
1116
reed@google.com5c3d1472011-02-22 19:12:23 +00001117 AutoValidateClip avc(this);
1118
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 fDeviceCMDirty = true;
1120 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001121 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122
1123 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001124 // for these simpler matrices, we can stay a rect ever after applying
1125 // the matrix. This means we don't have to a) make a path, and b) tell
1126 // the region code to scan-convert the path, only to discover that it
1127 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129
1130 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001131 fClipStack.clipDevRect(r, op, doAA);
1132 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001134 // since we're rotate or some such thing, we convert the rect to a path
1135 // and clip against that, since it can handle any matrix. However, to
1136 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1137 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 SkPath path;
1139
1140 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001141 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 }
1143}
1144
reed@google.com00177082011-10-12 14:34:30 +00001145static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001146 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001147 // base is used to limit the size (and therefore memory allocation) of the
1148 // region that results from scan converting devPath.
1149 SkRegion base;
1150
reed@google.com819c9212011-02-23 18:56:55 +00001151 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001152 // since we are intersect, we can do better (tighter) with currRgn's
1153 // bounds, than just using the device. However, if currRgn is complex,
1154 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001155 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001156 // FIXME: we should also be able to do this when currClip->isBW(),
1157 // but relaxing the test above triggers GM asserts in
1158 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1159 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001160 } else {
reed@google.com00177082011-10-12 14:34:30 +00001161 base.setRect(currClip->getBounds());
1162 SkRasterClip clip;
1163 clip.setPath(devPath, base, doAA);
1164 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001165 }
reed@google.com819c9212011-02-23 18:56:55 +00001166 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001167 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001168 if (!device) {
1169 return currClip->setEmpty();
1170 }
1171
junov@chromium.orga907ac32012-02-24 21:54:07 +00001172 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001173
1174 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001175 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001176 } else {
reed@google.com00177082011-10-12 14:34:30 +00001177 SkRasterClip clip;
1178 clip.setPath(devPath, base, doAA);
1179 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001180 }
1181 }
1182}
1183
reed@google.com4ed0fb72012-12-12 20:48:18 +00001184bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1185 if (rrect.isRect()) {
1186 // call the non-virtual version
1187 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1188 } else {
1189 SkPath path;
1190 path.addRRect(rrect);
1191 // call the non-virtual version
1192 return this->SkCanvas::clipPath(path, op, doAA);
1193 }
1194}
1195
reed@google.comc42d35d2011-10-12 11:57:42 +00001196bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001197#ifdef SK_ENABLE_CLIP_QUICKREJECT
1198 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1199 if (fMCRec->fRasterClip->isEmpty()) {
1200 return false;
1201 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001202
reed@google.com3b3e8952012-08-16 20:53:31 +00001203 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001204 fDeviceCMDirty = true;
1205 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001206
reed@google.comda17f752012-08-16 18:27:05 +00001207 fClipStack.clipEmpty();
1208 return fMCRec->fRasterClip->setEmpty();
1209 }
1210 }
1211#endif
1212
reed@google.com5c3d1472011-02-22 19:12:23 +00001213 AutoValidateClip avc(this);
1214
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 fDeviceCMDirty = true;
1216 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001217 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218
1219 SkPath devPath;
1220 path.transform(*fMCRec->fMatrix, &devPath);
1221
reed@google.comfe701122011-11-08 19:41:23 +00001222 // Check if the transfomation, or the original path itself
1223 // made us empty. Note this can also happen if we contained NaN
1224 // values. computing the bounds detects this, and will set our
1225 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1226 if (devPath.getBounds().isEmpty()) {
1227 // resetting the path will remove any NaN or other wanky values
1228 // that might upset our scan converter.
1229 devPath.reset();
1230 }
1231
reed@google.com5c3d1472011-02-22 19:12:23 +00001232 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001233 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001234
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001235 if (fAllowSimplifyClip) {
1236 devPath.reset();
1237 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1238 const SkClipStack* clipStack = getClipStack();
1239 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1240 const SkClipStack::Element* element;
1241 while ((element = iter.next())) {
1242 SkClipStack::Element::Type type = element->getType();
1243 if (type == SkClipStack::Element::kEmpty_Type) {
1244 continue;
1245 }
1246 SkPath operand;
1247 if (type == SkClipStack::Element::kRect_Type) {
1248 operand.addRect(element->getRect());
1249 } else if (type == SkClipStack::Element::kPath_Type) {
1250 operand = element->getPath();
1251 } else {
1252 SkDEBUGFAIL("Unexpected type.");
1253 }
1254 SkRegion::Op elementOp = element->getOp();
1255 if (elementOp == SkRegion::kReplace_Op) {
1256 devPath = operand;
1257 } else {
1258 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1259 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001260 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1261 // perhaps we need an API change to avoid this sort of mixed-signals about
1262 // clipping.
1263 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001264 }
1265 op = SkRegion::kReplace_Op;
1266 }
1267
reed@google.com00177082011-10-12 14:34:30 +00001268 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269}
1270
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001271bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1272 bool inverseFilled) {
1273 // This is for updating the clip conservatively using only bounds
1274 // information.
1275 // Contract:
1276 // The current clip must contain the true clip. The true
1277 // clip is the clip that would have normally been computed
1278 // by calls to clipPath and clipRRect
1279 // Objective:
1280 // Keep the current clip as small as possible without
1281 // breaking the contract, using only clip bounding rectangles
1282 // (for performance).
1283
1284 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1285 // don't have to worry about getting caught in a loop. Thus anywhere
1286 // we call a virtual method, we explicitly prefix it with
1287 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001288
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001289 if (inverseFilled) {
1290 switch (op) {
1291 case SkRegion::kIntersect_Op:
1292 case SkRegion::kDifference_Op:
1293 // These ops can only shrink the current clip. So leaving
1294 // the clip unchanges conservatively respects the contract.
1295 return this->getClipDeviceBounds(NULL);
1296 case SkRegion::kUnion_Op:
1297 case SkRegion::kReplace_Op:
1298 case SkRegion::kReverseDifference_Op:
1299 case SkRegion::kXOR_Op:
1300 {
1301 // These ops can grow the current clip up to the extents of
1302 // the input clip, which is inverse filled, so we just set
1303 // the current clip to the device bounds.
1304 SkRect deviceBounds;
1305 SkIRect deviceIBounds;
1306 this->getDevice()->getGlobalBounds(&deviceIBounds);
1307 deviceBounds = SkRect::MakeFromIRect(deviceIBounds);
1308 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1309 // set the clip in device space
1310 this->SkCanvas::setMatrix(SkMatrix::I());
1311 bool result = this->SkCanvas::clipRect(deviceBounds,
1312 SkRegion::kReplace_Op, false);
1313 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1314 return result;
1315 }
1316 default:
1317 SkASSERT(0); // unhandled op?
1318 }
1319 } else {
1320 // Not inverse filled
1321 switch (op) {
1322 case SkRegion::kIntersect_Op:
1323 case SkRegion::kUnion_Op:
1324 case SkRegion::kReplace_Op:
1325 return this->SkCanvas::clipRect(bounds, op, false);
1326 case SkRegion::kDifference_Op:
1327 // Difference can only shrink the current clip.
1328 // Leaving clip unchanged conservatively fullfills the contract.
1329 return this->getClipDeviceBounds(NULL);
1330 case SkRegion::kReverseDifference_Op:
1331 // To reverse, we swap in the bounds with a replace op.
1332 // As with difference, leave it unchanged.
1333 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1334 case SkRegion::kXOR_Op:
1335 // Be conservative, based on (A XOR B) always included in (A union B),
1336 // which is always included in (bounds(A) union bounds(B))
1337 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1338 default:
1339 SkASSERT(0); // unhandled op?
1340 }
1341 }
1342 return true;
1343}
1344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001346 AutoValidateClip avc(this);
1347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 fDeviceCMDirty = true;
1349 fLocalBoundsCompareTypeDirty = true;
1350
reed@google.com5c3d1472011-02-22 19:12:23 +00001351 // todo: signal fClipStack that we have a region, and therefore (I guess)
1352 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001353 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001354
reed@google.com00177082011-10-12 14:34:30 +00001355 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356}
1357
reed@google.com819c9212011-02-23 18:56:55 +00001358#ifdef SK_DEBUG
1359void SkCanvas::validateClip() const {
1360 // construct clipRgn from the clipstack
1361 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001362 if (!device) {
1363 SkASSERT(this->getTotalClip().isEmpty());
1364 return;
1365 }
1366
reed@google.com819c9212011-02-23 18:56:55 +00001367 SkIRect ir;
1368 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001369 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001370
robertphillips@google.com80214e22012-07-20 15:33:18 +00001371 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001372 const SkClipStack::Element* element;
1373 while ((element = iter.next()) != NULL) {
1374 switch (element->getType()) {
1375 case SkClipStack::Element::kPath_Type:
1376 clipPathHelper(this,
1377 &tmpClip,
1378 element->getPath(),
1379 element->getOp(),
1380 element->isAA());
1381 break;
1382 case SkClipStack::Element::kRect_Type:
1383 element->getRect().round(&ir);
1384 tmpClip.op(ir, element->getOp());
1385 break;
1386 case SkClipStack::Element::kEmpty_Type:
1387 tmpClip.setEmpty();
1388 break;
reed@google.com819c9212011-02-23 18:56:55 +00001389 }
1390 }
1391
reed@google.com6f8f2922011-03-04 22:27:10 +00001392#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001393 // now compare against the current rgn
1394 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001395 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001396#endif
reed@google.com819c9212011-02-23 18:56:55 +00001397}
1398#endif
1399
reed@google.com90c07ea2012-04-13 13:50:27 +00001400void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001401 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001402 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001403
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001404 static const SkRect kEmpty = { 0, 0, 0, 0 };
1405 while ((element = iter.next()) != NULL) {
1406 switch (element->getType()) {
1407 case SkClipStack::Element::kPath_Type:
1408 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1409 break;
1410 case SkClipStack::Element::kRect_Type:
1411 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1412 break;
1413 case SkClipStack::Element::kEmpty_Type:
1414 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1415 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001416 }
1417 }
1418}
1419
reed@google.com5c3d1472011-02-22 19:12:23 +00001420///////////////////////////////////////////////////////////////////////////////
1421
reed@google.com3b3e8952012-08-16 20:53:31 +00001422void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001424
reed@google.com3b3e8952012-08-16 20:53:31 +00001425 if (!this->getClipBounds(&r)) {
1426 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001428 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1429 SkScalarToCompareType(r.fTop),
1430 SkScalarToCompareType(r.fRight),
1431 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 }
1433}
1434
reed@google.com3b3e8952012-08-16 20:53:31 +00001435bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001436
reed@google.com16078632011-12-06 18:56:37 +00001437 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001438 return true;
1439
reed@google.com00177082011-10-12 14:34:30 +00001440 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 return true;
1442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443
tomhudson@google.com8d430182011-06-06 19:11:19 +00001444 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001445 SkRect dst;
1446 fMCRec->fMatrix->mapRect(&dst, rect);
1447 SkIRect idst;
1448 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001449 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001450 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001451 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001452
reed@android.coma380ae42009-07-21 01:17:02 +00001453 // for speed, do the most likely reject compares first
1454 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1455 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1456 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1457 return true;
1458 }
1459 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1460 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1461 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1462 return true;
1463 }
1464 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
reed@google.com3b3e8952012-08-16 20:53:31 +00001468bool SkCanvas::quickReject(const SkPath& path) const {
1469 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001472static inline int pinIntForScalar(int x) {
1473#ifdef SK_SCALAR_IS_FIXED
1474 if (x < SK_MinS16) {
1475 x = SK_MinS16;
1476 } else if (x > SK_MaxS16) {
1477 x = SK_MaxS16;
1478 }
1479#endif
1480 return x;
1481}
1482
reed@google.com3b3e8952012-08-16 20:53:31 +00001483bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001484 SkIRect ibounds;
1485 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486 return false;
1487 }
1488
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001489 SkMatrix inverse;
1490 // if we can't invert the CTM, we can't return local clip bounds
1491 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001492 if (bounds) {
1493 bounds->setEmpty();
1494 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001495 return false;
1496 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001498 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001499 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001500 // adjust it outwards in case we are antialiasing
1501 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001502
1503 // SkRect::iset() will correctly assert if we pass a value out of range
1504 // (when SkScalar==fixed), so we pin to legal values. This does not
1505 // really returnt the correct answer, but its the best we can do given
1506 // that we've promised to return SkRect (even though we support devices
1507 // that can be larger than 32K in width or height).
1508 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1509 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001510 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001511 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512 inverse.mapRect(bounds, r);
1513 }
1514 return true;
1515}
1516
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001517bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001518 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001519 if (clip.isEmpty()) {
1520 if (bounds) {
1521 bounds->setEmpty();
1522 }
1523 return false;
1524 }
1525
1526 if (NULL != bounds) {
1527 *bounds = clip.getBounds();
1528 }
1529 return true;
1530}
1531
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532const SkMatrix& SkCanvas::getTotalMatrix() const {
1533 return *fMCRec->fMatrix;
1534}
1535
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001536SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001537 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1538 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001539 return kComplex_ClipType;
1540}
1541
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001543 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544}
1545
bsalomon@google.come97f0852011-06-17 13:10:25 +00001546SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1547 int width, int height,
1548 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001549 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001550 if (device) {
1551 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1552 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001553 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001554 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001555 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556}
1557
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001558SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001559 int width, int height,
1560 bool isOpaque) {
1561 SkDevice* device = this->getDevice();
1562 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001563 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001564 } else {
1565 return NULL;
1566 }
1567}
1568
1569
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570//////////////////////////////////////////////////////////////////////////////
1571// These are the virtual drawing methods
1572//////////////////////////////////////////////////////////////////////////////
1573
reed@google.com2a981812011-04-14 18:59:28 +00001574void SkCanvas::clear(SkColor color) {
1575 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001576 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001577 while (iter.next()) {
1578 iter.fDevice->clear(color);
1579 }
1580}
1581
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001583 this->internalDrawPaint(paint);
1584}
1585
1586void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001587 CHECK_SHADER_NOSETCONTEXT(paint);
1588
reed@google.com4e2b3d32011-04-07 14:18:59 +00001589 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590
1591 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001592 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593 }
1594
reed@google.com4e2b3d32011-04-07 14:18:59 +00001595 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596}
1597
1598void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1599 const SkPaint& paint) {
1600 if ((long)count <= 0) {
1601 return;
1602 }
1603
reed@google.comea033602012-12-14 13:13:55 +00001604 CHECK_SHADER_NOSETCONTEXT(paint);
1605
reed@google.coma584aed2012-05-16 14:06:02 +00001606 if (paint.canComputeFastBounds()) {
1607 SkRect r;
1608 // special-case 2 points (common for drawing a single line)
1609 if (2 == count) {
1610 r.set(pts[0], pts[1]);
1611 } else {
1612 r.set(pts, count);
1613 }
1614 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001615 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001616 return;
1617 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001618 }
reed@google.coma584aed2012-05-16 14:06:02 +00001619
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 SkASSERT(pts != NULL);
1621
reed@google.com4e2b3d32011-04-07 14:18:59 +00001622 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001623
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001625 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 }
reed@google.com4b226022011-01-11 18:32:13 +00001627
reed@google.com4e2b3d32011-04-07 14:18:59 +00001628 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629}
1630
1631void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001632 CHECK_SHADER_NOSETCONTEXT(paint);
1633
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 if (paint.canComputeFastBounds()) {
1635 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001636 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637 return;
1638 }
1639 }
reed@google.com4b226022011-01-11 18:32:13 +00001640
reed@google.com4e2b3d32011-04-07 14:18:59 +00001641 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642
1643 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001644 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645 }
1646
reed@google.com4e2b3d32011-04-07 14:18:59 +00001647 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648}
1649
reed@google.com4ed0fb72012-12-12 20:48:18 +00001650void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001651 CHECK_SHADER_NOSETCONTEXT(paint);
1652
reed@google.com4ed0fb72012-12-12 20:48:18 +00001653 if (paint.canComputeFastBounds()) {
1654 SkRect storage;
1655 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1656 return;
1657 }
1658 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001659
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001660 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1661
1662 while (iter.next()) {
1663 iter.fDevice->drawOval(iter, oval, looper.paint());
1664 }
1665
1666 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001667}
1668
1669void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001670 CHECK_SHADER_NOSETCONTEXT(paint);
1671
reed@google.com4ed0fb72012-12-12 20:48:18 +00001672 if (paint.canComputeFastBounds()) {
1673 SkRect storage;
1674 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1675 return;
1676 }
1677 }
1678
1679 if (rrect.isRect()) {
1680 // call the non-virtual version
1681 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001682 return;
1683 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001684 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001685 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1686 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001687 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001688
1689 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1690
1691 while (iter.next()) {
1692 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1693 }
1694
1695 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001696}
1697
1698
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001700 CHECK_SHADER_NOSETCONTEXT(paint);
1701
reed@google.com93645112012-07-26 16:11:47 +00001702 if (!path.isFinite()) {
1703 return;
1704 }
1705
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001706 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001707 SkRect storage;
1708 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001709 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710 return;
1711 }
1712 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001713 if (path.isEmpty()) {
1714 if (path.isInverseFillType()) {
1715 this->internalDrawPaint(paint);
1716 }
1717 return;
1718 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719
reed@google.com4e2b3d32011-04-07 14:18:59 +00001720 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721
1722 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001723 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724 }
1725
reed@google.com4e2b3d32011-04-07 14:18:59 +00001726 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727}
1728
1729void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1730 const SkPaint* paint) {
1731 SkDEBUGCODE(bitmap.validate();)
1732
reed@google.com3d608122011-11-21 15:16:16 +00001733 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001734 SkRect bounds = {
1735 x, y,
1736 x + SkIntToScalar(bitmap.width()),
1737 y + SkIntToScalar(bitmap.height())
1738 };
1739 if (paint) {
1740 (void)paint->computeFastBounds(bounds, &bounds);
1741 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001742 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743 return;
1744 }
1745 }
reed@google.com4b226022011-01-11 18:32:13 +00001746
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747 SkMatrix matrix;
1748 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001749 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750}
1751
reed@google.com9987ec32011-09-07 11:57:52 +00001752// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001753void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001754 const SkRect& dst, const SkPaint* paint,
1755 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1757 return;
1758 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001759
reed@google.comea033602012-12-14 13:13:55 +00001760 CHECK_LOCKCOUNT_BALANCE(bitmap);
1761
reed@google.com3d608122011-11-21 15:16:16 +00001762 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001763 SkRect storage;
1764 const SkRect* bounds = &dst;
1765 if (paint) {
1766 bounds = &paint->computeFastBounds(dst, &storage);
1767 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001768 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001769 return;
1770 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 }
reed@google.com3d608122011-11-21 15:16:16 +00001772
reed@google.com33535f32012-09-25 15:37:50 +00001773 SkLazyPaint lazy;
1774 if (NULL == paint) {
1775 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001777
reed@google.com33535f32012-09-25 15:37:50 +00001778 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001779
reed@google.com33535f32012-09-25 15:37:50 +00001780 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001781 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001782 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001783
reed@google.com33535f32012-09-25 15:37:50 +00001784 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785}
1786
reed@google.com71121732012-09-18 15:14:33 +00001787void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001788 const SkRect& dst, const SkPaint* paint,
1789 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001790 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001791 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001792}
1793
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1795 const SkPaint* paint) {
1796 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001797 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798}
1799
reed@google.com9987ec32011-09-07 11:57:52 +00001800void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1801 const SkIRect& center, const SkRect& dst,
1802 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001803 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001804 SkRect storage;
1805 const SkRect* bounds = &dst;
1806 if (paint) {
1807 bounds = &paint->computeFastBounds(dst, &storage);
1808 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001809 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001810 return;
1811 }
1812 }
1813
reed@google.com9987ec32011-09-07 11:57:52 +00001814 const int32_t w = bitmap.width();
1815 const int32_t h = bitmap.height();
1816
1817 SkIRect c = center;
1818 // pin center to the bounds of the bitmap
1819 c.fLeft = SkMax32(0, center.fLeft);
1820 c.fTop = SkMax32(0, center.fTop);
1821 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1822 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1823
reed@google.com71121732012-09-18 15:14:33 +00001824 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001825 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001826 };
1827 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001828 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001829 };
reed@google.com9987ec32011-09-07 11:57:52 +00001830 SkScalar dstX[4] = {
1831 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1832 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1833 };
1834 SkScalar dstY[4] = {
1835 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1836 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1837 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001838
reed@google.com9987ec32011-09-07 11:57:52 +00001839 if (dstX[1] > dstX[2]) {
1840 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1841 dstX[2] = dstX[1];
1842 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001843
reed@google.com9987ec32011-09-07 11:57:52 +00001844 if (dstY[1] > dstY[2]) {
1845 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1846 dstY[2] = dstY[1];
1847 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001848
reed@google.com9987ec32011-09-07 11:57:52 +00001849 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001850 SkRect s, d;
1851
reed@google.com9987ec32011-09-07 11:57:52 +00001852 s.fTop = srcY[y];
1853 s.fBottom = srcY[y+1];
1854 d.fTop = dstY[y];
1855 d.fBottom = dstY[y+1];
1856 for (int x = 0; x < 3; x++) {
1857 s.fLeft = srcX[x];
1858 s.fRight = srcX[x+1];
1859 d.fLeft = dstX[x];
1860 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001861 this->internalDrawBitmapRect(bitmap, &s, d, paint,
1862 kNone_DrawBitmapRectflag);
reed@google.com9987ec32011-09-07 11:57:52 +00001863 }
1864 }
1865}
1866
1867void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1868 const SkRect& dst, const SkPaint* paint) {
1869 SkDEBUGCODE(bitmap.validate();)
1870
1871 // Need a device entry-point, so gpu can use a mesh
1872 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1873}
1874
reed@google.comf67e4cf2011-03-15 20:56:58 +00001875class SkDeviceFilteredPaint {
1876public:
1877 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1878 SkDevice::TextFlags flags;
1879 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001880 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001881 newPaint->setFlags(flags.fFlags);
1882 newPaint->setHinting(flags.fHinting);
1883 fPaint = newPaint;
1884 } else {
1885 fPaint = &paint;
1886 }
1887 }
1888
reed@google.comf67e4cf2011-03-15 20:56:58 +00001889 const SkPaint& paint() const { return *fPaint; }
1890
1891private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001892 const SkPaint* fPaint;
1893 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001894};
1895
bungeman@google.com52c748b2011-08-22 21:30:43 +00001896void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1897 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001898 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001899 draw.fDevice->drawRect(draw, r, paint);
1900 } else {
1901 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001902 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001903 draw.fDevice->drawRect(draw, r, p);
1904 }
1905}
1906
1907void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1908 const char text[], size_t byteLength,
1909 SkScalar x, SkScalar y) {
1910 SkASSERT(byteLength == 0 || text != NULL);
1911
1912 // nothing to draw
1913 if (text == NULL || byteLength == 0 ||
1914 draw.fClip->isEmpty() ||
1915 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1916 return;
1917 }
1918
1919 SkScalar width = 0;
1920 SkPoint start;
1921
1922 start.set(0, 0); // to avoid warning
1923 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1924 SkPaint::kStrikeThruText_Flag)) {
1925 width = paint.measureText(text, byteLength);
1926
1927 SkScalar offsetX = 0;
1928 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1929 offsetX = SkScalarHalf(width);
1930 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1931 offsetX = width;
1932 }
1933 start.set(x - offsetX, y);
1934 }
1935
1936 if (0 == width) {
1937 return;
1938 }
1939
1940 uint32_t flags = paint.getFlags();
1941
1942 if (flags & (SkPaint::kUnderlineText_Flag |
1943 SkPaint::kStrikeThruText_Flag)) {
1944 SkScalar textSize = paint.getTextSize();
1945 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1946 SkRect r;
1947
1948 r.fLeft = start.fX;
1949 r.fRight = start.fX + width;
1950
1951 if (flags & SkPaint::kUnderlineText_Flag) {
1952 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1953 start.fY);
1954 r.fTop = offset;
1955 r.fBottom = offset + height;
1956 DrawRect(draw, paint, r, textSize);
1957 }
1958 if (flags & SkPaint::kStrikeThruText_Flag) {
1959 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1960 start.fY);
1961 r.fTop = offset;
1962 r.fBottom = offset + height;
1963 DrawRect(draw, paint, r, textSize);
1964 }
1965 }
1966}
1967
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968void SkCanvas::drawText(const void* text, size_t byteLength,
1969 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001970 CHECK_SHADER_NOSETCONTEXT(paint);
1971
reed@google.com4e2b3d32011-04-07 14:18:59 +00001972 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973
1974 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001975 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001976 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001977 DrawTextDecorations(iter, dfp.paint(),
1978 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 }
1980
reed@google.com4e2b3d32011-04-07 14:18:59 +00001981 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982}
1983
1984void SkCanvas::drawPosText(const void* text, size_t byteLength,
1985 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001986 CHECK_SHADER_NOSETCONTEXT(paint);
1987
reed@google.com4e2b3d32011-04-07 14:18:59 +00001988 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001989
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001991 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001993 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001994 }
reed@google.com4b226022011-01-11 18:32:13 +00001995
reed@google.com4e2b3d32011-04-07 14:18:59 +00001996 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997}
1998
1999void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2000 const SkScalar xpos[], SkScalar constY,
2001 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002002 CHECK_SHADER_NOSETCONTEXT(paint);
2003
reed@google.com4e2b3d32011-04-07 14:18:59 +00002004 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002005
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002007 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002009 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002010 }
reed@google.com4b226022011-01-11 18:32:13 +00002011
reed@google.com4e2b3d32011-04-07 14:18:59 +00002012 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002013}
2014
2015void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2016 const SkPath& path, const SkMatrix* matrix,
2017 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002018 CHECK_SHADER_NOSETCONTEXT(paint);
2019
reed@google.com4e2b3d32011-04-07 14:18:59 +00002020 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002021
2022 while (iter.next()) {
2023 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002024 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002025 }
2026
reed@google.com4e2b3d32011-04-07 14:18:59 +00002027 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028}
2029
djsollen@google.com56c69772011-11-08 19:00:26 +00002030#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002031void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
2032 const SkPoint pos[], const SkPaint& paint,
2033 const SkPath& path, const SkMatrix* matrix) {
reed@google.comea033602012-12-14 13:13:55 +00002034 CHECK_SHADER_NOSETCONTEXT(paint);
2035
reed@google.com4e2b3d32011-04-07 14:18:59 +00002036 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002037
2038 while (iter.next()) {
2039 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002040 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002041 }
2042
reed@google.com4e2b3d32011-04-07 14:18:59 +00002043 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002044}
2045#endif
2046
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2048 const SkPoint verts[], const SkPoint texs[],
2049 const SkColor colors[], SkXfermode* xmode,
2050 const uint16_t indices[], int indexCount,
2051 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002052 CHECK_SHADER_NOSETCONTEXT(paint);
2053
reed@google.com4e2b3d32011-04-07 14:18:59 +00002054 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002055
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056 while (iter.next()) {
2057 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002058 colors, xmode, indices, indexCount,
2059 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002060 }
reed@google.com4b226022011-01-11 18:32:13 +00002061
reed@google.com4e2b3d32011-04-07 14:18:59 +00002062 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063}
2064
2065//////////////////////////////////////////////////////////////////////////////
2066// These methods are NOT virtual, and therefore must call back into virtual
2067// methods, rather than actually drawing themselves.
2068//////////////////////////////////////////////////////////////////////////////
2069
2070void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002071 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 SkPaint paint;
2073
2074 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002075 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002076 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077 }
2078 this->drawPaint(paint);
2079}
2080
reed@android.com845fdac2009-06-23 03:01:32 +00002081void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082 SkPaint paint;
2083
2084 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002085 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002086 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002087 }
2088 this->drawPaint(paint);
2089}
2090
2091void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2092 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002093
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094 pt.set(x, y);
2095 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2096}
2097
2098void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2099 SkPoint pt;
2100 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002101
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102 pt.set(x, y);
2103 paint.setColor(color);
2104 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2105}
2106
2107void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2108 const SkPaint& paint) {
2109 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002110
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 pts[0].set(x0, y0);
2112 pts[1].set(x1, y1);
2113 this->drawPoints(kLines_PointMode, 2, pts, paint);
2114}
2115
2116void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2117 SkScalar right, SkScalar bottom,
2118 const SkPaint& paint) {
2119 SkRect r;
2120
2121 r.set(left, top, right, bottom);
2122 this->drawRect(r, paint);
2123}
2124
2125void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2126 const SkPaint& paint) {
2127 if (radius < 0) {
2128 radius = 0;
2129 }
2130
2131 SkRect r;
2132 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002133 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134}
2135
2136void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2137 const SkPaint& paint) {
2138 if (rx > 0 && ry > 0) {
2139 if (paint.canComputeFastBounds()) {
2140 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002141 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 return;
2143 }
2144 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002145 SkRRect rrect;
2146 rrect.setRectXY(r, rx, ry);
2147 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148 } else {
2149 this->drawRect(r, paint);
2150 }
2151}
2152
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2154 SkScalar sweepAngle, bool useCenter,
2155 const SkPaint& paint) {
2156 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2157 this->drawOval(oval, paint);
2158 } else {
2159 SkPath path;
2160 if (useCenter) {
2161 path.moveTo(oval.centerX(), oval.centerY());
2162 }
2163 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2164 if (useCenter) {
2165 path.close();
2166 }
2167 this->drawPath(path, paint);
2168 }
2169}
2170
2171void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2172 const SkPath& path, SkScalar hOffset,
2173 SkScalar vOffset, const SkPaint& paint) {
2174 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002175
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176 matrix.setTranslate(hOffset, vOffset);
2177 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2178}
2179
reed@android.comf76bacf2009-05-13 14:00:33 +00002180///////////////////////////////////////////////////////////////////////////////
2181
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184}
2185
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186///////////////////////////////////////////////////////////////////////////////
2187///////////////////////////////////////////////////////////////////////////////
2188
2189SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002190 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191
2192 SkASSERT(canvas);
2193
2194 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2195 fDone = !fImpl->next();
2196}
2197
2198SkCanvas::LayerIter::~LayerIter() {
2199 fImpl->~SkDrawIter();
2200}
2201
2202void SkCanvas::LayerIter::next() {
2203 fDone = !fImpl->next();
2204}
2205
2206SkDevice* SkCanvas::LayerIter::device() const {
2207 return fImpl->getDevice();
2208}
2209
2210const SkMatrix& SkCanvas::LayerIter::matrix() const {
2211 return fImpl->getMatrix();
2212}
2213
2214const SkPaint& SkCanvas::LayerIter::paint() const {
2215 const SkPaint* paint = fImpl->getPaint();
2216 if (NULL == paint) {
2217 paint = &fDefaultPaint;
2218 }
2219 return *paint;
2220}
2221
2222const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2223int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2224int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002225
2226///////////////////////////////////////////////////////////////////////////////
2227
2228SkCanvas::ClipVisitor::~ClipVisitor() { }