blob: e80504cd394e5f2cdebdf6ae2759ec315a586405 [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;
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
219 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 DeviceCM* fLayer;
222 /* If there are any layers in the stack, this points to the top-most
223 one that is at or below this level in the stack (so we know what
224 bitmap/device to draw into from this level. This value is NOT
225 reference counted, since the real owner is either our fLayer field,
226 or a previous one in a lower level.)
227 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000228 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
230 MCRec(const MCRec* prev, int flags) {
231 if (NULL != prev) {
232 if (flags & SkCanvas::kMatrix_SaveFlag) {
233 fMatrixStorage = *prev->fMatrix;
234 fMatrix = &fMatrixStorage;
235 } else {
236 fMatrix = prev->fMatrix;
237 }
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000240 fRasterClipStorage = *prev->fRasterClip;
241 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 } else {
reed@google.com00177082011-10-12 14:34:30 +0000243 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
245
246 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000247 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 fTopLayer = prev->fTopLayer;
250 } else { // no prev
251 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000254 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = NULL;
256 fTopLayer = NULL;
257 }
258 fLayer = NULL;
259
260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000264 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDELETE(fLayer);
266 dec_rec();
267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
reed@google.com00177082011-10-12 14:34:30 +0000270 SkMatrix fMatrixStorage;
271 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
274class SkDrawIter : public SkDraw {
275public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000277 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000278 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 canvas->updateDeviceCMCache();
280
reed@google.com90c07ea2012-04-13 13:50:27 +0000281 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fBounder = canvas->getBounder();
283 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000284 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com4b226022011-01-11 18:32:13 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 bool next() {
288 // skip over recs with empty clips
289 if (fSkipEmptyClips) {
290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
291 fCurrLayer = fCurrLayer->fNext;
292 }
293 }
294
reed@google.comf68c5e22012-02-24 16:38:58 +0000295 const DeviceCM* rec = fCurrLayer;
296 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297
298 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
300 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 fDevice = rec->fDevice;
302 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000304 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 fCurrLayer = rec->fNext;
307 if (fBounder) {
308 fBounder->setClip(fClip);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313 }
314 return false;
315 }
reed@google.com4b226022011-01-11 18:32:13 +0000316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000318 int getX() const { return fDevice->getOrigin().x(); }
319 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 const SkMatrix& getMatrix() const { return *fMatrix; }
321 const SkRegion& getClip() const { return *fClip; }
322 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324private:
325 SkCanvas* fCanvas;
326 const DeviceCM* fCurrLayer;
327 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 SkBool8 fSkipEmptyClips;
329
330 typedef SkDraw INHERITED;
331};
332
333/////////////////////////////////////////////////////////////////////////////
334
335class AutoDrawLooper {
336public:
reed@google.com8926b162012-03-23 15:36:36 +0000337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
338 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000339 fCanvas = canvas;
340 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000344 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
reed@google.com8926b162012-03-23 15:36:36 +0000347 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
348 SkPaint tmp;
349 tmp.setImageFilter(fOrigPaint.getImageFilter());
350 // it would be nice if we had a guess at the bounds, instead of null
351 (void)canvas->internalSaveLayer(NULL, &tmp,
352 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
353 // we'll clear the imageFilter for the actual draws in next(), so
354 // it will only be applied during the restore().
355 fDoClearImageFilter = true;
356 }
357
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 if (fLooper) {
359 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000360 fIsSimple = false;
361 } else {
362 // can we be marked as simple?
363 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364 }
365 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000368 if (fDoClearImageFilter) {
369 fCanvas->internalRestore();
370 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000371 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000373
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 const SkPaint& paint() const {
375 SkASSERT(fPaint);
376 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000378
reed@google.com129ec222012-05-15 13:24:09 +0000379 bool next(SkDrawFilter::Type drawType) {
380 if (fDone) {
381 return false;
382 } else if (fIsSimple) {
383 fDone = true;
384 fPaint = &fOrigPaint;
385 return !fPaint->nothingToDraw();
386 } else {
387 return this->doNext(drawType);
388 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000389 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000392 SkLazyPaint fLazyPaint;
393 SkCanvas* fCanvas;
394 const SkPaint& fOrigPaint;
395 SkDrawLooper* fLooper;
396 SkDrawFilter* fFilter;
397 const SkPaint* fPaint;
398 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000399 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000400 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000401 bool fIsSimple;
402
403 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404};
405
reed@google.com129ec222012-05-15 13:24:09 +0000406bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000407 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000408 SkASSERT(!fIsSimple);
409 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
410
411 SkPaint* paint = fLazyPaint.set(fOrigPaint);
412
413 if (fDoClearImageFilter) {
414 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416
reed@google.com129ec222012-05-15 13:24:09 +0000417 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000419 return false;
420 }
421 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000422 if (!fFilter->filter(paint, drawType)) {
423 fDone = true;
424 return false;
425 }
reed@google.com129ec222012-05-15 13:24:09 +0000426 if (NULL == fLooper) {
427 // no looper means we only draw once
428 fDone = true;
429 }
430 }
431 fPaint = paint;
432
433 // if we only came in here for the imagefilter, mark us as done
434 if (!fLooper && !fFilter) {
435 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000436 }
437
438 // call this after any possible paint modifiers
439 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440 fPaint = NULL;
441 return false;
442 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 return true;
444}
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446/* Stack helper for managing a SkBounder. In the destructor, if we were
447 given a bounder, we call its commit() method, signifying that we are
448 done accumulating bounds for that draw.
449*/
450class SkAutoBounderCommit {
451public:
452 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
453 ~SkAutoBounderCommit() {
454 if (NULL != fBounder) {
455 fBounder->commit();
456 }
457 }
458private:
459 SkBounder* fBounder;
460};
461
462#include "SkColorPriv.h"
463
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464////////// macros to place around the internal draw calls //////////////////
465
reed@google.com8926b162012-03-23 15:36:36 +0000466#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000467 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000468 AutoDrawLooper looper(this, paint, true); \
469 while (looper.next(type)) { \
470 SkAutoBounderCommit ac(fBounder); \
471 SkDrawIter iter(this);
472
reed@google.com4e2b3d32011-04-07 14:18:59 +0000473#define LOOPER_BEGIN(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000474 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000475 AutoDrawLooper looper(this, paint); \
476 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 SkAutoBounderCommit ac(fBounder); \
478 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000479
reed@google.com4e2b3d32011-04-07 14:18:59 +0000480#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481
482////////////////////////////////////////////////////////////////////////////
483
484SkDevice* SkCanvas::init(SkDevice* device) {
485 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000486 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000488 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000489 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000490 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000491 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000492 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493
494 fMCRec = (MCRec*)fMCStack.push_back();
495 new (fMCRec) MCRec(NULL, 0);
496
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000497 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 fMCRec->fTopLayer = fMCRec->fLayer;
499 fMCRec->fNext = NULL;
500
reed@google.com97af1a62012-08-28 12:19:02 +0000501 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000502
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 return this->setDevice(device);
504}
505
reed@google.comcde92112011-07-06 20:00:52 +0000506SkCanvas::SkCanvas()
507: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000508 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000509
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000510 this->init(NULL);
511}
512
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000514 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 inc_canvas();
516
517 this->init(device);
518}
519
520SkCanvas::SkCanvas(const SkBitmap& bitmap)
521 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
522 inc_canvas();
523
reed@google.comcde92112011-07-06 20:00:52 +0000524 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525}
526
527SkCanvas::~SkCanvas() {
528 // free up the contents of our deque
529 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000530 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 this->internalRestore(); // restore the last, since we're going away
533
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000534 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000535 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000536
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 dec_canvas();
538}
539
540SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
541 SkRefCnt_SafeAssign(fBounder, bounder);
542 return bounder;
543}
544
545SkDrawFilter* SkCanvas::getDrawFilter() const {
546 return fMCRec->fFilter;
547}
548
549SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
550 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
551 return filter;
552}
553
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000554SkMetaData& SkCanvas::getMetaData() {
555 // metadata users are rare, so we lazily allocate it. If that changes we
556 // can decide to just make it a field in the device (rather than a ptr)
557 if (NULL == fMetaData) {
558 fMetaData = new SkMetaData;
559 }
560 return *fMetaData;
561}
562
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563///////////////////////////////////////////////////////////////////////////////
564
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000565void SkCanvas::flush() {
566 SkDevice* device = this->getDevice();
567 if (device) {
568 device->flush();
569 }
570}
571
reed@google.com210ce002011-11-01 14:24:23 +0000572SkISize SkCanvas::getDeviceSize() const {
573 SkDevice* d = this->getDevice();
574 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
575}
576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577SkDevice* SkCanvas::getDevice() const {
578 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000579 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 SkASSERT(rec && rec->fLayer);
581 return rec->fLayer->fDevice;
582}
583
reed@google.com0b53d592012-03-19 18:26:34 +0000584SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
585 if (updateMatrixClip) {
586 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
587 }
reed@google.com9266fed2011-03-30 00:18:03 +0000588 return fMCRec->fTopLayer->fDevice;
589}
590
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591SkDevice* SkCanvas::setDevice(SkDevice* device) {
592 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000593 SkDeque::F2BIter iter(fMCStack);
594 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 SkASSERT(rec && rec->fLayer);
596 SkDevice* rootDevice = rec->fLayer->fDevice;
597
598 if (rootDevice == device) {
599 return device;
600 }
reed@google.com4b226022011-01-11 18:32:13 +0000601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000603 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 }
605 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000606 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 }
608
609 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
610 rootDevice = device;
611
612 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 /* Now we update our initial region to have the bounds of the new device,
615 and then intersect all of the clips in our stack with these bounds,
616 to ensure that we can't draw outside of the device's bounds (and trash
617 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000618
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 NOTE: this is only a partial-fix, since if the new device is larger than
620 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000621 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
623 reconstruct the correct clips, so this approximation will have to do.
624 The caller really needs to restore() back to the base if they want to
625 accurately take advantage of the new device bounds.
626 */
627
reed@google.com42aea282012-03-28 16:19:15 +0000628 SkIRect bounds;
629 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000631 } else {
632 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 }
reed@google.com42aea282012-03-28 16:19:15 +0000634 // now jam our 1st clip to be bounds, and intersect the rest with that
635 rec->fRasterClip->setRect(bounds);
636 while ((rec = (MCRec*)iter.next()) != NULL) {
637 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
638 }
639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 return device;
641}
642
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000643bool SkCanvas::readPixels(SkBitmap* bitmap,
644 int x, int y,
645 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000646 SkDevice* device = this->getDevice();
647 if (!device) {
648 return false;
649 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000650 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000651}
652
bsalomon@google.comc6980972011-11-02 19:57:21 +0000653bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000654 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000655 if (!device) {
656 return false;
657 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000658
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000659 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000660 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000661 if (!bounds.intersect(srcRect)) {
662 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000663 }
664
665 SkBitmap tmp;
666 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
667 bounds.height());
668 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
669 bitmap->swap(tmp);
670 return true;
671 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000672 return false;
673 }
reed@google.com51df9e32010-12-23 19:29:18 +0000674}
675
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000676void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
677 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000678 SkDevice* device = this->getDevice();
679 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000680 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
681 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
682 device->accessBitmap(true);
683 device->writePixels(bitmap, x, y, config8888);
684 }
reed@google.com51df9e32010-12-23 19:29:18 +0000685 }
686}
687
junov@google.com4370aed2012-01-18 16:21:08 +0000688SkCanvas* SkCanvas::canvasForDrawIter() {
689 return this;
690}
691
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692//////////////////////////////////////////////////////////////////////////////
693
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694void SkCanvas::updateDeviceCMCache() {
695 if (fDeviceCMDirty) {
696 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000697 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000699
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000701 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000703 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000705 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 } while ((layer = layer->fNext) != NULL);
707 }
708 fDeviceCMDirty = false;
709 }
710}
711
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712///////////////////////////////////////////////////////////////////////////////
713
714int SkCanvas::internalSave(SaveFlags flags) {
715 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 MCRec* newTop = (MCRec*)fMCStack.push_back();
718 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 newTop->fNext = fMCRec;
721 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000722
reed@google.com5c3d1472011-02-22 19:12:23 +0000723 fClipStack.save();
724 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 return saveCount;
727}
728
729int SkCanvas::save(SaveFlags flags) {
730 // call shared impl
731 return this->internalSave(flags);
732}
733
734#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
735#define C16MASK (1 << SkBitmap::kRGB_565_Config)
736#define C8MASK (1 << SkBitmap::kA8_Config)
737
738static SkBitmap::Config resolve_config(SkCanvas* canvas,
739 const SkIRect& bounds,
740 SkCanvas::SaveFlags flags,
741 bool* isOpaque) {
742 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
743
744#if 0
745 // loop through and union all the configs we may draw into
746 uint32_t configMask = 0;
747 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
748 {
749 SkDevice* device = canvas->getLayerDevice(i);
750 if (device->intersects(bounds))
751 configMask |= 1 << device->config();
752 }
753
754 // if the caller wants alpha or fullcolor, we can't return 565
755 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
756 SkCanvas::kHasAlphaLayer_SaveFlag))
757 configMask &= ~C16MASK;
758
759 switch (configMask) {
760 case C8MASK: // if we only have A8, return that
761 return SkBitmap::kA8_Config;
762
763 case C16MASK: // if we only have 565, return that
764 return SkBitmap::kRGB_565_Config;
765
766 default:
767 return SkBitmap::kARGB_8888_Config; // default answer
768 }
769#else
770 return SkBitmap::kARGB_8888_Config; // default answer
771#endif
772}
773
774static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
775 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
776}
777
junov@chromium.orga907ac32012-02-24 21:54:07 +0000778bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
779 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000780 SkIRect clipBounds;
781 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000782 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000783 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000784 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 if (NULL != bounds) {
786 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 this->getTotalMatrix().mapRect(&r, *bounds);
789 r.roundOut(&ir);
790 // early exit if the layer's bounds are clipped out
791 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000792 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000793 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000794 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000795 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 }
797 } else { // no user bounds, so just use the clip
798 ir = clipBounds;
799 }
800
reed@google.com5c3d1472011-02-22 19:12:23 +0000801 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000802
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 // early exit if the clip is now empty
804 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000805 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000806 return false;
807 }
808
809 if (intersection) {
810 *intersection = ir;
811 }
812 return true;
813}
814
815int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
816 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000817 return this->internalSaveLayer(bounds, paint, flags, false);
818}
819
820int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
821 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000822 // do this before we create the layer. We don't call the public save() since
823 // that would invoke a possibly overridden virtual
824 int count = this->internalSave(flags);
825
826 fDeviceCMDirty = true;
827
828 SkIRect ir;
829 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 return count;
831 }
832
reed@google.comb55deeb2012-01-06 14:43:09 +0000833 // Kill the imagefilter if our device doesn't allow it
834 SkLazyPaint lazyP;
835 if (paint && paint->getImageFilter()) {
836 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000837 if (justForImageFilter) {
838 // early exit if the layer was just for the imageFilter
839 return count;
840 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000841 SkPaint* p = lazyP.set(*paint);
842 p->setImageFilter(NULL);
843 paint = p;
844 }
845 }
846
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 bool isOpaque;
848 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
849
reed@google.com76dd2772012-01-05 21:15:07 +0000850 SkDevice* device;
851 if (paint && paint->getImageFilter()) {
852 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
853 isOpaque);
854 } else {
855 device = this->createLayerDevice(config, ir.width(), ir.height(),
856 isOpaque);
857 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000858 if (NULL == device) {
859 SkDebugf("Unable to create device for layer.");
860 return count;
861 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000862
reed@google.com6f8f2922011-03-04 22:27:10 +0000863 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000864 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 device->unref();
866
867 layer->fNext = fMCRec->fTopLayer;
868 fMCRec->fLayer = layer;
869 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
870
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000871 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 return count;
873}
874
875int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
876 SaveFlags flags) {
877 if (0xFF == alpha) {
878 return this->saveLayer(bounds, NULL, flags);
879 } else {
880 SkPaint tmpPaint;
881 tmpPaint.setAlpha(alpha);
882 return this->saveLayer(bounds, &tmpPaint, flags);
883 }
884}
885
886void SkCanvas::restore() {
887 // check for underflow
888 if (fMCStack.count() > 1) {
889 this->internalRestore();
890 }
891}
892
893void SkCanvas::internalRestore() {
894 SkASSERT(fMCStack.count() != 0);
895
896 fDeviceCMDirty = true;
897 fLocalBoundsCompareTypeDirty = true;
898
reed@google.com5c3d1472011-02-22 19:12:23 +0000899 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000900 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 DeviceCM* layer = fMCRec->fLayer; // may be null
902 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
903 fMCRec->fLayer = NULL;
904
905 // now do the normal restore()
906 fMCRec->~MCRec(); // balanced in save()
907 fMCStack.pop_back();
908 fMCRec = (MCRec*)fMCStack.back();
909
910 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
911 since if we're being recorded, we don't want to record this (the
912 recorder will have already recorded the restore).
913 */
914 if (NULL != layer) {
915 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000916 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000917 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
918 layer->fPaint);
919 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000921
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000922 SkASSERT(fSaveLayerCount > 0);
923 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 }
925 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000926 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000927
928 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929}
930
931int SkCanvas::getSaveCount() const {
932 return fMCStack.count();
933}
934
935void SkCanvas::restoreToCount(int count) {
936 // sanity check
937 if (count < 1) {
938 count = 1;
939 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000940
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000941 int n = this->getSaveCount() - count;
942 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 this->restore();
944 }
945}
946
reed@google.com7c202932011-12-14 18:48:05 +0000947bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000948 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000949}
950
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951/////////////////////////////////////////////////////////////////////////////
952
953// can't draw it if its empty, or its too big for a fixed-point width or height
954static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +0000955 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956}
957
reed@android.comf2b98d62010-12-20 18:26:13 +0000958void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 const SkMatrix& matrix, const SkPaint* paint) {
960 if (reject_bitmap(bitmap)) {
961 return;
962 }
963
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000964 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000966 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000968 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969}
970
reed@google.com8926b162012-03-23 15:36:36 +0000971void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
972 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 SkPaint tmp;
974 if (NULL == paint) {
975 tmp.setDither(true);
976 paint = &tmp;
977 }
reed@google.com4b226022011-01-11 18:32:13 +0000978
reed@google.com8926b162012-03-23 15:36:36 +0000979 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000981 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000982 paint = &looper.paint();
983 SkImageFilter* filter = paint->getImageFilter();
984 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000985 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000986 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000987 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000988 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000989 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000990 SkPaint tmpUnfiltered(*paint);
991 tmpUnfiltered.setImageFilter(NULL);
992 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000993 }
994 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000995 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000996 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000998 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999}
1000
reed@google.com8926b162012-03-23 15:36:36 +00001001void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1002 const SkPaint* paint) {
1003 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001004 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001005
reed@google.com8926b162012-03-23 15:36:36 +00001006 if (reject_bitmap(bitmap)) {
1007 return;
1008 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001009
reed@google.com8926b162012-03-23 15:36:36 +00001010 SkPaint tmp;
1011 if (NULL == paint) {
1012 paint = &tmp;
1013 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001014
reed@google.com8926b162012-03-23 15:36:36 +00001015 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001016
reed@google.com8926b162012-03-23 15:36:36 +00001017 while (iter.next()) {
1018 paint = &looper.paint();
1019 SkImageFilter* filter = paint->getImageFilter();
1020 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1021 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001022 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001023 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001024 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1025 &dst, &pos)) {
1026 SkPaint tmpUnfiltered(*paint);
1027 tmpUnfiltered.setImageFilter(NULL);
1028 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1029 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001030 }
1031 } else {
1032 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1033 }
1034 }
1035 LOOPER_END
1036}
1037
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038/////////////////////////////////////////////////////////////////////////////
1039
1040bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1041 fDeviceCMDirty = true;
1042 fLocalBoundsCompareTypeDirty = true;
1043 return fMCRec->fMatrix->preTranslate(dx, dy);
1044}
1045
1046bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1047 fDeviceCMDirty = true;
1048 fLocalBoundsCompareTypeDirty = true;
1049 return fMCRec->fMatrix->preScale(sx, sy);
1050}
1051
1052bool SkCanvas::rotate(SkScalar degrees) {
1053 fDeviceCMDirty = true;
1054 fLocalBoundsCompareTypeDirty = true;
1055 return fMCRec->fMatrix->preRotate(degrees);
1056}
1057
1058bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1059 fDeviceCMDirty = true;
1060 fLocalBoundsCompareTypeDirty = true;
1061 return fMCRec->fMatrix->preSkew(sx, sy);
1062}
1063
1064bool SkCanvas::concat(const SkMatrix& matrix) {
1065 fDeviceCMDirty = true;
1066 fLocalBoundsCompareTypeDirty = true;
1067 return fMCRec->fMatrix->preConcat(matrix);
1068}
1069
1070void SkCanvas::setMatrix(const SkMatrix& matrix) {
1071 fDeviceCMDirty = true;
1072 fLocalBoundsCompareTypeDirty = true;
1073 *fMCRec->fMatrix = matrix;
1074}
1075
1076// this is not virtual, so it must call a virtual method so that subclasses
1077// will see its action
1078void SkCanvas::resetMatrix() {
1079 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001080
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 matrix.reset();
1082 this->setMatrix(matrix);
1083}
1084
1085//////////////////////////////////////////////////////////////////////////////
1086
reed@google.comc42d35d2011-10-12 11:57:42 +00001087bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001088#ifdef SK_ENABLE_CLIP_QUICKREJECT
1089 if (SkRegion::kIntersect_Op == op) {
1090 if (fMCRec->fRasterClip->isEmpty()) {
1091 return false;
1092 }
1093
reed@google.com3b3e8952012-08-16 20:53:31 +00001094 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001095 fDeviceCMDirty = true;
1096 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001097
1098 fClipStack.clipEmpty();
1099 return fMCRec->fRasterClip->setEmpty();
1100 }
1101 }
1102#endif
1103
reed@google.com5c3d1472011-02-22 19:12:23 +00001104 AutoValidateClip avc(this);
1105
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 fDeviceCMDirty = true;
1107 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001108 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109
1110 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001111 // for these simpler matrices, we can stay a rect ever after applying
1112 // the matrix. This means we don't have to a) make a path, and b) tell
1113 // the region code to scan-convert the path, only to discover that it
1114 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116
1117 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001118 fClipStack.clipDevRect(r, op, doAA);
1119 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001121 // since we're rotate or some such thing, we convert the rect to a path
1122 // and clip against that, since it can handle any matrix. However, to
1123 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1124 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 SkPath path;
1126
1127 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001128 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 }
1130}
1131
reed@google.com00177082011-10-12 14:34:30 +00001132static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001133 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001134 // base is used to limit the size (and therefore memory allocation) of the
1135 // region that results from scan converting devPath.
1136 SkRegion base;
1137
reed@google.com819c9212011-02-23 18:56:55 +00001138 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001139 // since we are intersect, we can do better (tighter) with currRgn's
1140 // bounds, than just using the device. However, if currRgn is complex,
1141 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001142 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001143 // FIXME: we should also be able to do this when currClip->isBW(),
1144 // but relaxing the test above triggers GM asserts in
1145 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1146 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001147 } else {
reed@google.com00177082011-10-12 14:34:30 +00001148 base.setRect(currClip->getBounds());
1149 SkRasterClip clip;
1150 clip.setPath(devPath, base, doAA);
1151 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001152 }
reed@google.com819c9212011-02-23 18:56:55 +00001153 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001154 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001155 if (!device) {
1156 return currClip->setEmpty();
1157 }
1158
junov@chromium.orga907ac32012-02-24 21:54:07 +00001159 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001160
1161 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001162 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001163 } else {
reed@google.com00177082011-10-12 14:34:30 +00001164 SkRasterClip clip;
1165 clip.setPath(devPath, base, doAA);
1166 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001167 }
1168 }
1169}
1170
reed@google.com4ed0fb72012-12-12 20:48:18 +00001171bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1172 if (rrect.isRect()) {
1173 // call the non-virtual version
1174 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1175 } else {
1176 SkPath path;
1177 path.addRRect(rrect);
1178 // call the non-virtual version
1179 return this->SkCanvas::clipPath(path, op, doAA);
1180 }
1181}
1182
reed@google.comc42d35d2011-10-12 11:57:42 +00001183bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001184#ifdef SK_ENABLE_CLIP_QUICKREJECT
1185 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1186 if (fMCRec->fRasterClip->isEmpty()) {
1187 return false;
1188 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001189
reed@google.com3b3e8952012-08-16 20:53:31 +00001190 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001191 fDeviceCMDirty = true;
1192 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001193
reed@google.comda17f752012-08-16 18:27:05 +00001194 fClipStack.clipEmpty();
1195 return fMCRec->fRasterClip->setEmpty();
1196 }
1197 }
1198#endif
1199
reed@google.com5c3d1472011-02-22 19:12:23 +00001200 AutoValidateClip avc(this);
1201
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 fDeviceCMDirty = true;
1203 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001204 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205
1206 SkPath devPath;
1207 path.transform(*fMCRec->fMatrix, &devPath);
1208
reed@google.comfe701122011-11-08 19:41:23 +00001209 // Check if the transfomation, or the original path itself
1210 // made us empty. Note this can also happen if we contained NaN
1211 // values. computing the bounds detects this, and will set our
1212 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1213 if (devPath.getBounds().isEmpty()) {
1214 // resetting the path will remove any NaN or other wanky values
1215 // that might upset our scan converter.
1216 devPath.reset();
1217 }
1218
reed@google.com5c3d1472011-02-22 19:12:23 +00001219 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001220 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001221
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001222 if (fAllowSimplifyClip) {
1223 devPath.reset();
1224 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1225 const SkClipStack* clipStack = getClipStack();
1226 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1227 const SkClipStack::Element* element;
1228 while ((element = iter.next())) {
1229 SkClipStack::Element::Type type = element->getType();
1230 if (type == SkClipStack::Element::kEmpty_Type) {
1231 continue;
1232 }
1233 SkPath operand;
1234 if (type == SkClipStack::Element::kRect_Type) {
1235 operand.addRect(element->getRect());
1236 } else if (type == SkClipStack::Element::kPath_Type) {
1237 operand = element->getPath();
1238 } else {
1239 SkDEBUGFAIL("Unexpected type.");
1240 }
1241 SkRegion::Op elementOp = element->getOp();
1242 if (elementOp == SkRegion::kReplace_Op) {
1243 devPath = operand;
1244 } else {
1245 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1246 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001247 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1248 // perhaps we need an API change to avoid this sort of mixed-signals about
1249 // clipping.
1250 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001251 }
1252 op = SkRegion::kReplace_Op;
1253 }
1254
reed@google.com00177082011-10-12 14:34:30 +00001255 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256}
1257
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001258bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1259 bool inverseFilled) {
1260 // This is for updating the clip conservatively using only bounds
1261 // information.
1262 // Contract:
1263 // The current clip must contain the true clip. The true
1264 // clip is the clip that would have normally been computed
1265 // by calls to clipPath and clipRRect
1266 // Objective:
1267 // Keep the current clip as small as possible without
1268 // breaking the contract, using only clip bounding rectangles
1269 // (for performance).
1270
1271 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1272 // don't have to worry about getting caught in a loop. Thus anywhere
1273 // we call a virtual method, we explicitly prefix it with
1274 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001275
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001276 if (inverseFilled) {
1277 switch (op) {
1278 case SkRegion::kIntersect_Op:
1279 case SkRegion::kDifference_Op:
1280 // These ops can only shrink the current clip. So leaving
1281 // the clip unchanges conservatively respects the contract.
1282 return this->getClipDeviceBounds(NULL);
1283 case SkRegion::kUnion_Op:
1284 case SkRegion::kReplace_Op:
1285 case SkRegion::kReverseDifference_Op:
1286 case SkRegion::kXOR_Op:
1287 {
1288 // These ops can grow the current clip up to the extents of
1289 // the input clip, which is inverse filled, so we just set
1290 // the current clip to the device bounds.
1291 SkRect deviceBounds;
1292 SkIRect deviceIBounds;
1293 this->getDevice()->getGlobalBounds(&deviceIBounds);
1294 deviceBounds = SkRect::MakeFromIRect(deviceIBounds);
1295 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1296 // set the clip in device space
1297 this->SkCanvas::setMatrix(SkMatrix::I());
1298 bool result = this->SkCanvas::clipRect(deviceBounds,
1299 SkRegion::kReplace_Op, false);
1300 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1301 return result;
1302 }
1303 default:
1304 SkASSERT(0); // unhandled op?
1305 }
1306 } else {
1307 // Not inverse filled
1308 switch (op) {
1309 case SkRegion::kIntersect_Op:
1310 case SkRegion::kUnion_Op:
1311 case SkRegion::kReplace_Op:
1312 return this->SkCanvas::clipRect(bounds, op, false);
1313 case SkRegion::kDifference_Op:
1314 // Difference can only shrink the current clip.
1315 // Leaving clip unchanged conservatively fullfills the contract.
1316 return this->getClipDeviceBounds(NULL);
1317 case SkRegion::kReverseDifference_Op:
1318 // To reverse, we swap in the bounds with a replace op.
1319 // As with difference, leave it unchanged.
1320 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1321 case SkRegion::kXOR_Op:
1322 // Be conservative, based on (A XOR B) always included in (A union B),
1323 // which is always included in (bounds(A) union bounds(B))
1324 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1325 default:
1326 SkASSERT(0); // unhandled op?
1327 }
1328 }
1329 return true;
1330}
1331
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001333 AutoValidateClip avc(this);
1334
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 fDeviceCMDirty = true;
1336 fLocalBoundsCompareTypeDirty = true;
1337
reed@google.com5c3d1472011-02-22 19:12:23 +00001338 // todo: signal fClipStack that we have a region, and therefore (I guess)
1339 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001340 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001341
reed@google.com00177082011-10-12 14:34:30 +00001342 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343}
1344
reed@google.com819c9212011-02-23 18:56:55 +00001345#ifdef SK_DEBUG
1346void SkCanvas::validateClip() const {
1347 // construct clipRgn from the clipstack
1348 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001349 if (!device) {
1350 SkASSERT(this->getTotalClip().isEmpty());
1351 return;
1352 }
1353
reed@google.com819c9212011-02-23 18:56:55 +00001354 SkIRect ir;
1355 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001356 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001357
robertphillips@google.com80214e22012-07-20 15:33:18 +00001358 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001359 const SkClipStack::Element* element;
1360 while ((element = iter.next()) != NULL) {
1361 switch (element->getType()) {
1362 case SkClipStack::Element::kPath_Type:
1363 clipPathHelper(this,
1364 &tmpClip,
1365 element->getPath(),
1366 element->getOp(),
1367 element->isAA());
1368 break;
1369 case SkClipStack::Element::kRect_Type:
1370 element->getRect().round(&ir);
1371 tmpClip.op(ir, element->getOp());
1372 break;
1373 case SkClipStack::Element::kEmpty_Type:
1374 tmpClip.setEmpty();
1375 break;
reed@google.com819c9212011-02-23 18:56:55 +00001376 }
1377 }
1378
reed@google.com6f8f2922011-03-04 22:27:10 +00001379#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001380 // now compare against the current rgn
1381 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001382 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001383#endif
reed@google.com819c9212011-02-23 18:56:55 +00001384}
1385#endif
1386
reed@google.com90c07ea2012-04-13 13:50:27 +00001387void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001388 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001389 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001390
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001391 static const SkRect kEmpty = { 0, 0, 0, 0 };
1392 while ((element = iter.next()) != NULL) {
1393 switch (element->getType()) {
1394 case SkClipStack::Element::kPath_Type:
1395 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1396 break;
1397 case SkClipStack::Element::kRect_Type:
1398 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1399 break;
1400 case SkClipStack::Element::kEmpty_Type:
1401 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1402 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001403 }
1404 }
1405}
1406
reed@google.com5c3d1472011-02-22 19:12:23 +00001407///////////////////////////////////////////////////////////////////////////////
1408
reed@google.com3b3e8952012-08-16 20:53:31 +00001409void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001411
reed@google.com3b3e8952012-08-16 20:53:31 +00001412 if (!this->getClipBounds(&r)) {
1413 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001415 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1416 SkScalarToCompareType(r.fTop),
1417 SkScalarToCompareType(r.fRight),
1418 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 }
1420}
1421
reed@google.com3b3e8952012-08-16 20:53:31 +00001422bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001423
reed@google.com16078632011-12-06 18:56:37 +00001424 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001425 return true;
1426
reed@google.com00177082011-10-12 14:34:30 +00001427 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 return true;
1429 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430
tomhudson@google.com8d430182011-06-06 19:11:19 +00001431 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001432 SkRect dst;
1433 fMCRec->fMatrix->mapRect(&dst, rect);
1434 SkIRect idst;
1435 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001436 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001437 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001438 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001439
reed@android.coma380ae42009-07-21 01:17:02 +00001440 // for speed, do the most likely reject compares first
1441 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1442 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1443 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1444 return true;
1445 }
1446 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1447 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1448 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1449 return true;
1450 }
1451 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453}
1454
reed@google.com3b3e8952012-08-16 20:53:31 +00001455bool SkCanvas::quickReject(const SkPath& path) const {
1456 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457}
1458
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001459static inline int pinIntForScalar(int x) {
1460#ifdef SK_SCALAR_IS_FIXED
1461 if (x < SK_MinS16) {
1462 x = SK_MinS16;
1463 } else if (x > SK_MaxS16) {
1464 x = SK_MaxS16;
1465 }
1466#endif
1467 return x;
1468}
1469
reed@google.com3b3e8952012-08-16 20:53:31 +00001470bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001471 SkIRect ibounds;
1472 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 return false;
1474 }
1475
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001476 SkMatrix inverse;
1477 // if we can't invert the CTM, we can't return local clip bounds
1478 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001479 if (bounds) {
1480 bounds->setEmpty();
1481 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001482 return false;
1483 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001485 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001486 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001487 // adjust it outwards in case we are antialiasing
1488 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001489
1490 // SkRect::iset() will correctly assert if we pass a value out of range
1491 // (when SkScalar==fixed), so we pin to legal values. This does not
1492 // really returnt the correct answer, but its the best we can do given
1493 // that we've promised to return SkRect (even though we support devices
1494 // that can be larger than 32K in width or height).
1495 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1496 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001497 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001498 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 inverse.mapRect(bounds, r);
1500 }
1501 return true;
1502}
1503
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001504bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001505 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001506 if (clip.isEmpty()) {
1507 if (bounds) {
1508 bounds->setEmpty();
1509 }
1510 return false;
1511 }
1512
1513 if (NULL != bounds) {
1514 *bounds = clip.getBounds();
1515 }
1516 return true;
1517}
1518
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519const SkMatrix& SkCanvas::getTotalMatrix() const {
1520 return *fMCRec->fMatrix;
1521}
1522
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001523SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001524 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1525 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001526 return kComplex_ClipType;
1527}
1528
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001530 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531}
1532
bsalomon@google.come97f0852011-06-17 13:10:25 +00001533SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1534 int width, int height,
1535 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001536 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001537 if (device) {
1538 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1539 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001540 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001541 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001542 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543}
1544
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001545SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001546 int width, int height,
1547 bool isOpaque) {
1548 SkDevice* device = this->getDevice();
1549 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001550 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001551 } else {
1552 return NULL;
1553 }
1554}
1555
1556
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557//////////////////////////////////////////////////////////////////////////////
1558// These are the virtual drawing methods
1559//////////////////////////////////////////////////////////////////////////////
1560
reed@google.com2a981812011-04-14 18:59:28 +00001561void SkCanvas::clear(SkColor color) {
1562 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001563 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001564 while (iter.next()) {
1565 iter.fDevice->clear(color);
1566 }
1567}
1568
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001570 this->internalDrawPaint(paint);
1571}
1572
1573void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001574 CHECK_SHADER_NOSETCONTEXT(paint);
1575
reed@google.com4e2b3d32011-04-07 14:18:59 +00001576 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577
1578 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001579 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580 }
1581
reed@google.com4e2b3d32011-04-07 14:18:59 +00001582 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583}
1584
1585void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1586 const SkPaint& paint) {
1587 if ((long)count <= 0) {
1588 return;
1589 }
1590
reed@google.comea033602012-12-14 13:13:55 +00001591 CHECK_SHADER_NOSETCONTEXT(paint);
1592
reed@google.coma584aed2012-05-16 14:06:02 +00001593 if (paint.canComputeFastBounds()) {
1594 SkRect r;
1595 // special-case 2 points (common for drawing a single line)
1596 if (2 == count) {
1597 r.set(pts[0], pts[1]);
1598 } else {
1599 r.set(pts, count);
1600 }
1601 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001602 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001603 return;
1604 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001605 }
reed@google.coma584aed2012-05-16 14:06:02 +00001606
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 SkASSERT(pts != NULL);
1608
reed@google.com4e2b3d32011-04-07 14:18:59 +00001609 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001610
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001612 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 }
reed@google.com4b226022011-01-11 18:32:13 +00001614
reed@google.com4e2b3d32011-04-07 14:18:59 +00001615 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616}
1617
1618void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001619 CHECK_SHADER_NOSETCONTEXT(paint);
1620
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 if (paint.canComputeFastBounds()) {
1622 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001623 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 return;
1625 }
1626 }
reed@google.com4b226022011-01-11 18:32:13 +00001627
reed@google.com4e2b3d32011-04-07 14:18:59 +00001628 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629
1630 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001631 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 }
1633
reed@google.com4e2b3d32011-04-07 14:18:59 +00001634 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635}
1636
reed@google.com4ed0fb72012-12-12 20:48:18 +00001637void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001638 CHECK_SHADER_NOSETCONTEXT(paint);
1639
reed@google.com4ed0fb72012-12-12 20:48:18 +00001640 if (paint.canComputeFastBounds()) {
1641 SkRect storage;
1642 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1643 return;
1644 }
1645 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001646
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001647 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
1648
1649 while (iter.next()) {
1650 iter.fDevice->drawOval(iter, oval, looper.paint());
1651 }
1652
1653 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001654}
1655
1656void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001657 CHECK_SHADER_NOSETCONTEXT(paint);
1658
reed@google.com4ed0fb72012-12-12 20:48:18 +00001659 if (paint.canComputeFastBounds()) {
1660 SkRect storage;
1661 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1662 return;
1663 }
1664 }
1665
1666 if (rrect.isRect()) {
1667 // call the non-virtual version
1668 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001669 return;
1670 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001671 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001672 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1673 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001674 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001675
1676 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type)
1677
1678 while (iter.next()) {
1679 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1680 }
1681
1682 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001683}
1684
1685
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001687 CHECK_SHADER_NOSETCONTEXT(paint);
1688
reed@google.com93645112012-07-26 16:11:47 +00001689 if (!path.isFinite()) {
1690 return;
1691 }
1692
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001693 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001694 SkRect storage;
1695 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001696 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697 return;
1698 }
1699 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001700 if (path.isEmpty()) {
1701 if (path.isInverseFillType()) {
1702 this->internalDrawPaint(paint);
1703 }
1704 return;
1705 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706
reed@google.com4e2b3d32011-04-07 14:18:59 +00001707 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708
1709 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001710 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 }
1712
reed@google.com4e2b3d32011-04-07 14:18:59 +00001713 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714}
1715
1716void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1717 const SkPaint* paint) {
1718 SkDEBUGCODE(bitmap.validate();)
1719
reed@google.com3d608122011-11-21 15:16:16 +00001720 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001721 SkRect bounds = {
1722 x, y,
1723 x + SkIntToScalar(bitmap.width()),
1724 y + SkIntToScalar(bitmap.height())
1725 };
1726 if (paint) {
1727 (void)paint->computeFastBounds(bounds, &bounds);
1728 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001729 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 return;
1731 }
1732 }
reed@google.com4b226022011-01-11 18:32:13 +00001733
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734 SkMatrix matrix;
1735 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001736 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737}
1738
reed@google.com9987ec32011-09-07 11:57:52 +00001739// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001740void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001741 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1743 return;
1744 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001745
reed@google.comea033602012-12-14 13:13:55 +00001746 CHECK_LOCKCOUNT_BALANCE(bitmap);
1747
reed@google.com3d608122011-11-21 15:16:16 +00001748 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001749 SkRect storage;
1750 const SkRect* bounds = &dst;
1751 if (paint) {
1752 bounds = &paint->computeFastBounds(dst, &storage);
1753 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001754 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001755 return;
1756 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 }
reed@google.com3d608122011-11-21 15:16:16 +00001758
reed@google.com33535f32012-09-25 15:37:50 +00001759 SkLazyPaint lazy;
1760 if (NULL == paint) {
1761 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001763
reed@google.com33535f32012-09-25 15:37:50 +00001764 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001765
reed@google.com33535f32012-09-25 15:37:50 +00001766 while (iter.next()) {
1767 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001768 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001769
reed@google.com33535f32012-09-25 15:37:50 +00001770 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771}
1772
reed@google.com71121732012-09-18 15:14:33 +00001773void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1774 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001775 SkDEBUGCODE(bitmap.validate();)
1776 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1777}
1778
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1780 const SkPaint* paint) {
1781 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001782 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783}
1784
reed@android.comf2b98d62010-12-20 18:26:13 +00001785void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1786 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001788 CHECK_LOCKCOUNT_BALANCE(bitmap);
reed@android.com9b039062009-02-11 15:09:58 +00001789
reed@google.com4e2b3d32011-04-07 14:18:59 +00001790 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001791
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001793 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794 }
reed@android.com9b039062009-02-11 15:09:58 +00001795
reed@google.com4e2b3d32011-04-07 14:18:59 +00001796 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797}
1798
reed@google.com9987ec32011-09-07 11:57:52 +00001799void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1800 const SkIRect& center, const SkRect& dst,
1801 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001802 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001803 SkRect storage;
1804 const SkRect* bounds = &dst;
1805 if (paint) {
1806 bounds = &paint->computeFastBounds(dst, &storage);
1807 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001808 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001809 return;
1810 }
1811 }
1812
reed@google.com9987ec32011-09-07 11:57:52 +00001813 const int32_t w = bitmap.width();
1814 const int32_t h = bitmap.height();
1815
1816 SkIRect c = center;
1817 // pin center to the bounds of the bitmap
1818 c.fLeft = SkMax32(0, center.fLeft);
1819 c.fTop = SkMax32(0, center.fTop);
1820 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1821 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1822
reed@google.com71121732012-09-18 15:14:33 +00001823 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001824 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001825 };
1826 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001827 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001828 };
reed@google.com9987ec32011-09-07 11:57:52 +00001829 SkScalar dstX[4] = {
1830 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1831 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1832 };
1833 SkScalar dstY[4] = {
1834 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1835 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1836 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001837
reed@google.com9987ec32011-09-07 11:57:52 +00001838 if (dstX[1] > dstX[2]) {
1839 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1840 dstX[2] = dstX[1];
1841 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001842
reed@google.com9987ec32011-09-07 11:57:52 +00001843 if (dstY[1] > dstY[2]) {
1844 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1845 dstY[2] = dstY[1];
1846 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001847
reed@google.com9987ec32011-09-07 11:57:52 +00001848 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001849 SkRect s, d;
1850
reed@google.com9987ec32011-09-07 11:57:52 +00001851 s.fTop = srcY[y];
1852 s.fBottom = srcY[y+1];
1853 d.fTop = dstY[y];
1854 d.fBottom = dstY[y+1];
1855 for (int x = 0; x < 3; x++) {
1856 s.fLeft = srcX[x];
1857 s.fRight = srcX[x+1];
1858 d.fLeft = dstX[x];
1859 d.fRight = dstX[x+1];
1860 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1861 }
1862 }
1863}
1864
1865void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1866 const SkRect& dst, const SkPaint* paint) {
1867 SkDEBUGCODE(bitmap.validate();)
1868
1869 // Need a device entry-point, so gpu can use a mesh
1870 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1871}
1872
reed@google.comf67e4cf2011-03-15 20:56:58 +00001873class SkDeviceFilteredPaint {
1874public:
1875 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1876 SkDevice::TextFlags flags;
1877 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001878 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001879 newPaint->setFlags(flags.fFlags);
1880 newPaint->setHinting(flags.fHinting);
1881 fPaint = newPaint;
1882 } else {
1883 fPaint = &paint;
1884 }
1885 }
1886
reed@google.comf67e4cf2011-03-15 20:56:58 +00001887 const SkPaint& paint() const { return *fPaint; }
1888
1889private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001890 const SkPaint* fPaint;
1891 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001892};
1893
bungeman@google.com52c748b2011-08-22 21:30:43 +00001894void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1895 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001896 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001897 draw.fDevice->drawRect(draw, r, paint);
1898 } else {
1899 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001900 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001901 draw.fDevice->drawRect(draw, r, p);
1902 }
1903}
1904
1905void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1906 const char text[], size_t byteLength,
1907 SkScalar x, SkScalar y) {
1908 SkASSERT(byteLength == 0 || text != NULL);
1909
1910 // nothing to draw
1911 if (text == NULL || byteLength == 0 ||
1912 draw.fClip->isEmpty() ||
1913 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1914 return;
1915 }
1916
1917 SkScalar width = 0;
1918 SkPoint start;
1919
1920 start.set(0, 0); // to avoid warning
1921 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1922 SkPaint::kStrikeThruText_Flag)) {
1923 width = paint.measureText(text, byteLength);
1924
1925 SkScalar offsetX = 0;
1926 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1927 offsetX = SkScalarHalf(width);
1928 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1929 offsetX = width;
1930 }
1931 start.set(x - offsetX, y);
1932 }
1933
1934 if (0 == width) {
1935 return;
1936 }
1937
1938 uint32_t flags = paint.getFlags();
1939
1940 if (flags & (SkPaint::kUnderlineText_Flag |
1941 SkPaint::kStrikeThruText_Flag)) {
1942 SkScalar textSize = paint.getTextSize();
1943 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1944 SkRect r;
1945
1946 r.fLeft = start.fX;
1947 r.fRight = start.fX + width;
1948
1949 if (flags & SkPaint::kUnderlineText_Flag) {
1950 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1951 start.fY);
1952 r.fTop = offset;
1953 r.fBottom = offset + height;
1954 DrawRect(draw, paint, r, textSize);
1955 }
1956 if (flags & SkPaint::kStrikeThruText_Flag) {
1957 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1958 start.fY);
1959 r.fTop = offset;
1960 r.fBottom = offset + height;
1961 DrawRect(draw, paint, r, textSize);
1962 }
1963 }
1964}
1965
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966void SkCanvas::drawText(const void* text, size_t byteLength,
1967 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001968 CHECK_SHADER_NOSETCONTEXT(paint);
1969
reed@google.com4e2b3d32011-04-07 14:18:59 +00001970 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971
1972 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001973 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001974 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001975 DrawTextDecorations(iter, dfp.paint(),
1976 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977 }
1978
reed@google.com4e2b3d32011-04-07 14:18:59 +00001979 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980}
1981
1982void SkCanvas::drawPosText(const void* text, size_t byteLength,
1983 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001984 CHECK_SHADER_NOSETCONTEXT(paint);
1985
reed@google.com4e2b3d32011-04-07 14:18:59 +00001986 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001987
reed@android.com8a1c16f2008-12-17 15:59:43 +00001988 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001989 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001991 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992 }
reed@google.com4b226022011-01-11 18:32:13 +00001993
reed@google.com4e2b3d32011-04-07 14:18:59 +00001994 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995}
1996
1997void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1998 const SkScalar xpos[], SkScalar constY,
1999 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002000 CHECK_SHADER_NOSETCONTEXT(paint);
2001
reed@google.com4e2b3d32011-04-07 14:18:59 +00002002 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002003
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002005 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002007 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008 }
reed@google.com4b226022011-01-11 18:32:13 +00002009
reed@google.com4e2b3d32011-04-07 14:18:59 +00002010 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011}
2012
2013void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2014 const SkPath& path, const SkMatrix* matrix,
2015 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002016 CHECK_SHADER_NOSETCONTEXT(paint);
2017
reed@google.com4e2b3d32011-04-07 14:18:59 +00002018 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019
2020 while (iter.next()) {
2021 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002022 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002023 }
2024
reed@google.com4e2b3d32011-04-07 14:18:59 +00002025 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026}
2027
djsollen@google.com56c69772011-11-08 19:00:26 +00002028#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002029void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
2030 const SkPoint pos[], const SkPaint& paint,
2031 const SkPath& path, const SkMatrix* matrix) {
reed@google.comea033602012-12-14 13:13:55 +00002032 CHECK_SHADER_NOSETCONTEXT(paint);
2033
reed@google.com4e2b3d32011-04-07 14:18:59 +00002034 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002035
2036 while (iter.next()) {
2037 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002038 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002039 }
2040
reed@google.com4e2b3d32011-04-07 14:18:59 +00002041 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002042}
2043#endif
2044
reed@android.com8a1c16f2008-12-17 15:59:43 +00002045void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2046 const SkPoint verts[], const SkPoint texs[],
2047 const SkColor colors[], SkXfermode* xmode,
2048 const uint16_t indices[], int indexCount,
2049 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002050 CHECK_SHADER_NOSETCONTEXT(paint);
2051
reed@google.com4e2b3d32011-04-07 14:18:59 +00002052 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00002053
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054 while (iter.next()) {
2055 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002056 colors, xmode, indices, indexCount,
2057 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058 }
reed@google.com4b226022011-01-11 18:32:13 +00002059
reed@google.com4e2b3d32011-04-07 14:18:59 +00002060 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061}
2062
2063//////////////////////////////////////////////////////////////////////////////
2064// These methods are NOT virtual, and therefore must call back into virtual
2065// methods, rather than actually drawing themselves.
2066//////////////////////////////////////////////////////////////////////////////
2067
2068void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002069 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070 SkPaint paint;
2071
2072 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002073 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002074 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 }
2076 this->drawPaint(paint);
2077}
2078
reed@android.com845fdac2009-06-23 03:01:32 +00002079void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080 SkPaint paint;
2081
2082 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002083 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002084 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 }
2086 this->drawPaint(paint);
2087}
2088
2089void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2090 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002091
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 pt.set(x, y);
2093 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2094}
2095
2096void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2097 SkPoint pt;
2098 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002099
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 pt.set(x, y);
2101 paint.setColor(color);
2102 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2103}
2104
2105void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2106 const SkPaint& paint) {
2107 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002108
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109 pts[0].set(x0, y0);
2110 pts[1].set(x1, y1);
2111 this->drawPoints(kLines_PointMode, 2, pts, paint);
2112}
2113
2114void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2115 SkScalar right, SkScalar bottom,
2116 const SkPaint& paint) {
2117 SkRect r;
2118
2119 r.set(left, top, right, bottom);
2120 this->drawRect(r, paint);
2121}
2122
2123void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2124 const SkPaint& paint) {
2125 if (radius < 0) {
2126 radius = 0;
2127 }
2128
2129 SkRect r;
2130 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002131 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132}
2133
2134void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2135 const SkPaint& paint) {
2136 if (rx > 0 && ry > 0) {
2137 if (paint.canComputeFastBounds()) {
2138 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002139 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140 return;
2141 }
2142 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002143 SkRRect rrect;
2144 rrect.setRectXY(r, rx, ry);
2145 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146 } else {
2147 this->drawRect(r, paint);
2148 }
2149}
2150
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2152 SkScalar sweepAngle, bool useCenter,
2153 const SkPaint& paint) {
2154 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2155 this->drawOval(oval, paint);
2156 } else {
2157 SkPath path;
2158 if (useCenter) {
2159 path.moveTo(oval.centerX(), oval.centerY());
2160 }
2161 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2162 if (useCenter) {
2163 path.close();
2164 }
2165 this->drawPath(path, paint);
2166 }
2167}
2168
2169void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2170 const SkPath& path, SkScalar hOffset,
2171 SkScalar vOffset, const SkPaint& paint) {
2172 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002173
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 matrix.setTranslate(hOffset, vOffset);
2175 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2176}
2177
reed@android.comf76bacf2009-05-13 14:00:33 +00002178///////////////////////////////////////////////////////////////////////////////
2179
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182}
2183
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184///////////////////////////////////////////////////////////////////////////////
2185///////////////////////////////////////////////////////////////////////////////
2186
2187SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002188 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189
2190 SkASSERT(canvas);
2191
2192 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2193 fDone = !fImpl->next();
2194}
2195
2196SkCanvas::LayerIter::~LayerIter() {
2197 fImpl->~SkDrawIter();
2198}
2199
2200void SkCanvas::LayerIter::next() {
2201 fDone = !fImpl->next();
2202}
2203
2204SkDevice* SkCanvas::LayerIter::device() const {
2205 return fImpl->getDevice();
2206}
2207
2208const SkMatrix& SkCanvas::LayerIter::matrix() const {
2209 return fImpl->getMatrix();
2210}
2211
2212const SkPaint& SkCanvas::LayerIter::paint() const {
2213 const SkPaint* paint = fImpl->getPaint();
2214 if (NULL == paint) {
2215 paint = &fDefaultPaint;
2216 }
2217 return *paint;
2218}
2219
2220const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2221int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2222int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002223
2224///////////////////////////////////////////////////////////////////////////////
2225
2226SkCanvas::ClipVisitor::~ClipVisitor() { }