blob: 3b7cc5bea0a4217001ffb94dda6fefc32ebefcc0 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkBounder.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
94class AutoCheckNoSetContext {
95public:
96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
97 this->assertNoSetContext(fPaint);
98 }
99 ~AutoCheckNoSetContext() {
100 this->assertNoSetContext(fPaint);
101 }
102
103private:
104 const SkPaint& fPaint;
105
106 void assertNoSetContext(const SkPaint& paint) {
107 SkShader* s = paint.getShader();
108 if (s) {
109 SkASSERT(!s->setContextHasBeenCalled());
110 }
111 }
112};
113
114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
119 #define CHECK_SHADER_NOSETCONTEXT(paint)
120#endif
121
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000122typedef SkTLazy<SkPaint> SkLazyPaint;
123
reed@google.com97af1a62012-08-28 12:19:02 +0000124void SkCanvas::predrawNotify() {
125 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000127 }
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000132/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 The clip/matrix/proc are fields that reflect the top of the save/restore
134 stack. Whenever the canvas changes, it marks a dirty flag, and then before
135 these are used (assuming we're not on a layer) we rebuild these cache
136 values: they reflect the top of the save stack, but translated and clipped
137 by the device's XY offset and bitmap-bounds.
138*/
139struct DeviceCM {
140 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000141 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000142 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000146 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 : fNext(NULL) {
148 if (NULL != device) {
149 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000150 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@google.com4b226022011-01-11 18:32:13 +0000152 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000156 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000158 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fDevice->unref();
160 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000161 SkDELETE(fPaint);
162 }
reed@google.com4b226022011-01-11 18:32:13 +0000163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
165 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000166 int x = fDevice->getOrigin().x();
167 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 int width = fDevice->width();
169 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if ((x | y) == 0) {
172 fMatrix = &totalMatrix;
173 fClip = totalClip;
174 } else {
175 fMatrixStorage = totalMatrix;
176 fMatrixStorage.postTranslate(SkIntToScalar(-x),
177 SkIntToScalar(-y));
178 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 totalClip.translate(-x, -y, &fClip);
181 }
182
reed@google.com045e62d2011-10-24 12:19:46 +0000183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000186
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkRegion::kDifference_Op);
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#ifdef SK_DEBUG
195 if (!fClip.isEmpty()) {
196 SkIRect deviceR;
197 deviceR.set(0, 0, width, height);
198 SkASSERT(deviceR.contains(fClip.getBounds()));
199 }
200#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000201 }
202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000204 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207/* This is the record we keep for each save/restore level in the stack.
208 Since a level optionally copies the matrix and/or stack, we have pointers
209 for these fields. If the value is copied for this level, the copy is
210 stored in the ...Storage field, and the pointer points to that. If the
211 value is not copied for this level, we ignore ...Storage, and just point
212 at the corresponding value in the previous level in the stack.
213*/
214class SkCanvas::MCRec {
215public:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000216 int fFlags;
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
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000230 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 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
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000317 SkBaseDevice* 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,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000338 bool skipLayerForImageFilter = false,
339 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fCanvas = canvas;
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());
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000350 (void)canvas->internalSaveLayer(bounds, &tmp,
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000351 SkCanvas::kARGB_ClipLayer_SaveFlag, true, false);
reed@google.com8926b162012-03-23 15:36:36 +0000352 // we'll clear the imageFilter for the actual draws in next(), so
353 // it will only be applied during the restore().
354 fDoClearImageFilter = true;
355 }
356
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000357 if (SkDrawLooper* looper = paint.getLooper()) {
358 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
359 looper->contextSize());
360 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000361 fIsSimple = false;
362 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000363 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000364 // can we be marked as simple?
365 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000366 }
367 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000370 if (fDoClearImageFilter) {
371 fCanvas->internalRestore();
372 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000375
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 const SkPaint& paint() const {
377 SkASSERT(fPaint);
378 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000380
reed@google.com129ec222012-05-15 13:24:09 +0000381 bool next(SkDrawFilter::Type drawType) {
382 if (fDone) {
383 return false;
384 } else if (fIsSimple) {
385 fDone = true;
386 fPaint = &fOrigPaint;
387 return !fPaint->nothingToDraw();
388 } else {
389 return this->doNext(drawType);
390 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000391 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000394 SkLazyPaint fLazyPaint;
395 SkCanvas* fCanvas;
396 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000397 SkDrawFilter* fFilter;
398 const SkPaint* fPaint;
399 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000400 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000401 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000402 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000403 SkDrawLooper::Context* fLooperContext;
404 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000405
406 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407};
408
reed@google.com129ec222012-05-15 13:24:09 +0000409bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000410 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000411 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000412 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000413
414 SkPaint* paint = fLazyPaint.set(fOrigPaint);
415
416 if (fDoClearImageFilter) {
417 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000420 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000422 return false;
423 }
424 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000425 if (!fFilter->filter(paint, drawType)) {
426 fDone = true;
427 return false;
428 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000429 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000430 // no looper means we only draw once
431 fDone = true;
432 }
433 }
434 fPaint = paint;
435
436 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000437 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000438 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000439 }
440
441 // call this after any possible paint modifiers
442 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 fPaint = NULL;
444 return false;
445 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000446 return true;
447}
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449/* Stack helper for managing a SkBounder. In the destructor, if we were
450 given a bounder, we call its commit() method, signifying that we are
451 done accumulating bounds for that draw.
452*/
453class SkAutoBounderCommit {
454public:
455 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
456 ~SkAutoBounderCommit() {
457 if (NULL != fBounder) {
458 fBounder->commit();
459 }
460 }
461private:
462 SkBounder* fBounder;
463};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000464#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465
466#include "SkColorPriv.h"
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468////////// macros to place around the internal draw calls //////////////////
469
reed@google.com8926b162012-03-23 15:36:36 +0000470#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000471 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000472 AutoDrawLooper looper(this, paint, true); \
473 while (looper.next(type)) { \
474 SkAutoBounderCommit ac(fBounder); \
475 SkDrawIter iter(this);
476
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000477#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000478 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000479 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000480 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 SkAutoBounderCommit ac(fBounder); \
482 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000483
reed@google.com4e2b3d32011-04-07 14:18:59 +0000484#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485
486////////////////////////////////////////////////////////////////////////////
487
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000488SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000490 fCachedLocalClipBounds.setEmpty();
491 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000492 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000493 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000494 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000495 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000496 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000497 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
500 new (fMCRec) MCRec(NULL, 0);
501
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000502 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reed@google.com97af1a62012-08-28 12:19:02 +0000505 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000506
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000507 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508}
509
reed@google.comcde92112011-07-06 20:00:52 +0000510SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000511 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
512{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000513 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000514
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000515 this->init(NULL);
516}
517
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000518SkCanvas::SkCanvas(int width, int height)
519 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
520{
521 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000522
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000523 SkBitmap bitmap;
reed@google.com900ecf22014-02-20 20:55:37 +0000524 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
526}
527
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000528SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000529 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
530{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 inc_canvas();
532
533 this->init(device);
534}
535
536SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
538{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 inc_canvas();
540
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000541 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542}
543
544SkCanvas::~SkCanvas() {
545 // free up the contents of our deque
546 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000547 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000548
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 this->internalRestore(); // restore the last, since we're going away
550
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000551 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000552 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000553
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 dec_canvas();
555}
556
557SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
558 SkRefCnt_SafeAssign(fBounder, bounder);
559 return bounder;
560}
561
562SkDrawFilter* SkCanvas::getDrawFilter() const {
563 return fMCRec->fFilter;
564}
565
566SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
567 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
568 return filter;
569}
570
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000571SkMetaData& SkCanvas::getMetaData() {
572 // metadata users are rare, so we lazily allocate it. If that changes we
573 // can decide to just make it a field in the device (rather than a ptr)
574 if (NULL == fMetaData) {
575 fMetaData = new SkMetaData;
576 }
577 return *fMetaData;
578}
579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580///////////////////////////////////////////////////////////////////////////////
581
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000582void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000583 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000584 if (device) {
585 device->flush();
586 }
587}
588
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000589SkISize SkCanvas::getTopLayerSize() const {
590 SkBaseDevice* d = this->getTopDevice();
591 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
592}
593
594SkIPoint SkCanvas::getTopLayerOrigin() const {
595 SkBaseDevice* d = this->getTopDevice();
596 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
597}
598
599SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000600 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000601 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
602}
603
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000604SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000606 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 SkASSERT(rec && rec->fLayer);
608 return rec->fLayer->fDevice;
609}
610
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000611SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000612 if (updateMatrixClip) {
613 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
614 }
reed@google.com9266fed2011-03-30 00:18:03 +0000615 return fMCRec->fTopLayer->fDevice;
616}
617
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000618SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000620 SkDeque::F2BIter iter(fMCStack);
621 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000623 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624
625 if (rootDevice == device) {
626 return device;
627 }
reed@google.com4b226022011-01-11 18:32:13 +0000628
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000630 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 }
632 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000633 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
635
636 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
637 rootDevice = device;
638
639 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 /* Now we update our initial region to have the bounds of the new device,
642 and then intersect all of the clips in our stack with these bounds,
643 to ensure that we can't draw outside of the device's bounds (and trash
644 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 NOTE: this is only a partial-fix, since if the new device is larger than
647 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000648 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
650 reconstruct the correct clips, so this approximation will have to do.
651 The caller really needs to restore() back to the base if they want to
652 accurately take advantage of the new device bounds.
653 */
654
reed@google.com42aea282012-03-28 16:19:15 +0000655 SkIRect bounds;
656 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000658 } else {
659 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 }
reed@google.com42aea282012-03-28 16:19:15 +0000661 // now jam our 1st clip to be bounds, and intersect the rest with that
662 rec->fRasterClip->setRect(bounds);
663 while ((rec = (MCRec*)iter.next()) != NULL) {
664 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
665 }
666
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 return device;
668}
669
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000670bool SkCanvas::readPixels(SkBitmap* bitmap,
671 int x, int y,
672 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000673 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000674 if (!device) {
675 return false;
676 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000677 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000678}
679
bsalomon@google.comc6980972011-11-02 19:57:21 +0000680bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000681 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000682 if (!device) {
683 return false;
684 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000685
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000686 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000687 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000688 if (!bounds.intersect(srcRect)) {
689 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000690 }
691
692 SkBitmap tmp;
693 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
694 bounds.height());
695 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
696 bitmap->swap(tmp);
697 return true;
698 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000699 return false;
700 }
reed@google.com51df9e32010-12-23 19:29:18 +0000701}
702
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000703#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000704void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
705 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000706 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000707 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000708 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
709 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
710 device->accessBitmap(true);
711 device->writePixels(bitmap, x, y, config8888);
712 }
reed@google.com51df9e32010-12-23 19:29:18 +0000713 }
714}
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000715#endif
716
717bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
718 if (bitmap.getTexture()) {
719 return false;
720 }
721 SkBitmap bm(bitmap);
722 bm.lockPixels();
723 if (bm.getPixels()) {
724 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
725 }
726 return false;
727}
728
729bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
730 int x, int y) {
731 switch (origInfo.colorType()) {
732 case kUnknown_SkColorType:
733 case kIndex_8_SkColorType:
734 return false;
735 default:
736 break;
737 }
738 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
739 return false;
740 }
741
742 const SkISize size = this->getBaseLayerSize();
743 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
744 if (!target.intersect(0, 0, size.width(), size.height())) {
745 return false;
746 }
747
748 SkBaseDevice* device = this->getDevice();
749 if (!device) {
750 return false;
751 }
752
753 SkImageInfo info = origInfo;
754 // the intersect may have shrunk info's logical size
755 info.fWidth = target.width();
756 info.fHeight = target.height();
757
758 // if x or y are negative, then we have to adjust pixels
759 if (x > 0) {
760 x = 0;
761 }
762 if (y > 0) {
763 y = 0;
764 }
765 // here x,y are either 0 or negative
766 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
767
768 // The device can assert that the requested area is always contained in its bounds
769 return device->writePixelsDirect(info, pixels, rowBytes, target.x(), target.y());
770}
reed@google.com51df9e32010-12-23 19:29:18 +0000771
junov@google.com4370aed2012-01-18 16:21:08 +0000772SkCanvas* SkCanvas::canvasForDrawIter() {
773 return this;
774}
775
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776//////////////////////////////////////////////////////////////////////////////
777
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778void SkCanvas::updateDeviceCMCache() {
779 if (fDeviceCMDirty) {
780 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000781 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000785 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000787 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000789 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 } while ((layer = layer->fNext) != NULL);
791 }
792 fDeviceCMDirty = false;
793 }
794}
795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796///////////////////////////////////////////////////////////////////////////////
797
798int SkCanvas::internalSave(SaveFlags flags) {
799 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000800
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 MCRec* newTop = (MCRec*)fMCStack.push_back();
802 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000803
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000805
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000806 if (SkCanvas::kClip_SaveFlag & flags) {
807 fClipStack.save();
808 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 return saveCount;
811}
812
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000813void SkCanvas::onSave(SaveFlags) {
814 // Do nothing. Subclasses may do something.
815}
816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000818 this->onSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 // call shared impl
820 return this->internalSave(flags);
821}
822
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000824#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000826#else
827 return true;
828#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829}
830
junov@chromium.orga907ac32012-02-24 21:54:07 +0000831bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000832 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000833 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000834 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000835 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000836 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000837 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000838
839 if (imageFilter) {
840 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
841 // Filters may grow the bounds beyond the device bounds.
842 op = SkRegion::kReplace_Op;
843 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000844 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 if (NULL != bounds) {
846 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000847
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 this->getTotalMatrix().mapRect(&r, *bounds);
849 r.roundOut(&ir);
850 // early exit if the layer's bounds are clipped out
851 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000852 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000853 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000854 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 }
857 } else { // no user bounds, so just use the clip
858 ir = clipBounds;
859 }
860
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000861 if (bounds_affects_clip(flags)) {
862 fClipStack.clipDevRect(ir, op);
863 // early exit if the clip is now empty
864 if (!fMCRec->fRasterClip->op(ir, op)) {
865 return false;
866 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000867 }
868
869 if (intersection) {
870 *intersection = ir;
871 }
872 return true;
873}
874
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000875bool SkCanvas::onSaveLayer(const SkRect*, const SkPaint*, SaveFlags) {
876 // Do nothing. Subclasses may do something.
877 return true;
878}
879
junov@chromium.orga907ac32012-02-24 21:54:07 +0000880int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
881 SaveFlags flags) {
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000882 // Overriding classes may return false to signal that we don't need to create a layer.
883 bool skipLayer = !this->onSaveLayer(bounds, paint, flags);
884 return this->internalSaveLayer(bounds, paint, flags, false, skipLayer);
reed@google.com8926b162012-03-23 15:36:36 +0000885}
886
reed@google.com76f10a32014-02-05 15:32:21 +0000887static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000888 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000889 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000890 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000891}
892
reed@google.com8926b162012-03-23 15:36:36 +0000893int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000894 SaveFlags flags, bool justForImageFilter, bool skipLayer) {
reed@google.comb93ba452014-03-10 19:47:58 +0000895#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
896 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
897#endif
898
junov@chromium.orga907ac32012-02-24 21:54:07 +0000899 // do this before we create the layer. We don't call the public save() since
900 // that would invoke a possibly overridden virtual
901 int count = this->internalSave(flags);
902
903 fDeviceCMDirty = true;
904
905 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000906 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 return count;
908 }
909
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000910 // FIXME: do onSaveLayer() overriders returning false really care about the clipRectBounds()
911 // call above?
912 if (skipLayer)
913 return count;
914
reed@google.comb55deeb2012-01-06 14:43:09 +0000915 // Kill the imagefilter if our device doesn't allow it
916 SkLazyPaint lazyP;
917 if (paint && paint->getImageFilter()) {
918 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000919 if (justForImageFilter) {
920 // early exit if the layer was just for the imageFilter
921 return count;
922 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000923 SkPaint* p = lazyP.set(*paint);
924 p->setImageFilter(NULL);
925 paint = p;
926 }
927 }
928
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000929 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
930 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
931 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000933 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000934 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000935 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000936 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000937 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000938 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000939 if (NULL == device) {
940 SkDebugf("Unable to create device for layer.");
941 return count;
942 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000943
reed@google.com6f8f2922011-03-04 22:27:10 +0000944 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000945 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 device->unref();
947
948 layer->fNext = fMCRec->fTopLayer;
949 fMCRec->fLayer = layer;
950 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
951
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000952 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953 return count;
954}
955
956int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
957 SaveFlags flags) {
958 if (0xFF == alpha) {
959 return this->saveLayer(bounds, NULL, flags);
960 } else {
961 SkPaint tmpPaint;
962 tmpPaint.setAlpha(alpha);
963 return this->saveLayer(bounds, &tmpPaint, flags);
964 }
965}
966
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000967void SkCanvas::onRestore() {
968 // Do nothing. Subclasses may do something.
969}
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971void SkCanvas::restore() {
972 // check for underflow
973 if (fMCStack.count() > 1) {
commit-bot@chromium.org4fcd92d2014-03-11 23:47:35 +0000974 this->onRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 this->internalRestore();
976 }
977}
978
979void SkCanvas::internalRestore() {
980 SkASSERT(fMCStack.count() != 0);
981
982 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000983 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000985 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
986 fClipStack.restore();
987 }
988
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000989 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 DeviceCM* layer = fMCRec->fLayer; // may be null
991 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
992 fMCRec->fLayer = NULL;
993
994 // now do the normal restore()
995 fMCRec->~MCRec(); // balanced in save()
996 fMCStack.pop_back();
997 fMCRec = (MCRec*)fMCStack.back();
998
999 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1000 since if we're being recorded, we don't want to record this (the
1001 recorder will have already recorded the restore).
1002 */
1003 if (NULL != layer) {
1004 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001005 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001006 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1007 layer->fPaint);
1008 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001010
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001011 SkASSERT(fSaveLayerCount > 0);
1012 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 }
1014 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001015 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016}
1017
1018int SkCanvas::getSaveCount() const {
1019 return fMCStack.count();
1020}
1021
1022void SkCanvas::restoreToCount(int count) {
1023 // sanity check
1024 if (count < 1) {
1025 count = 1;
1026 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001027
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001028 int n = this->getSaveCount() - count;
1029 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 this->restore();
1031 }
1032}
1033
reed@google.com7c202932011-12-14 18:48:05 +00001034bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001035 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001036}
1037
reed@google.com76f10a32014-02-05 15:32:21 +00001038SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1039 return this->onNewSurface(info);
1040}
1041
1042SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1043 SkBaseDevice* dev = this->getDevice();
1044 return dev ? dev->newSurface(info) : NULL;
1045}
1046
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001047SkImageInfo SkCanvas::imageInfo() const {
1048 SkBaseDevice* dev = this->getDevice();
1049 if (dev) {
1050 return dev->imageInfo();
1051 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001052 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001053 }
1054}
1055
1056const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1057 return this->onPeekPixels(info, rowBytes);
1058}
1059
1060const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1061 SkBaseDevice* dev = this->getDevice();
1062 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1063}
1064
1065SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1066 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1067 if (NULL == fAddr) {
1068 fInfo = canvas->imageInfo();
1069 if (kUnknown_SkColorType == fInfo.colorType() ||
1070 !fBitmap.allocPixels(fInfo))
1071 {
1072 return; // failure, fAddr is NULL
1073 }
1074 fBitmap.lockPixels();
1075 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1076 return; // failure, fAddr is NULL
1077 }
1078 fAddr = fBitmap.getPixels();
1079 fRowBytes = fBitmap.rowBytes();
1080 }
1081 SkASSERT(fAddr); // success
1082}
1083
1084bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1085 if (fAddr) {
1086 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1087 NULL, NULL);
1088 } else {
1089 bitmap->reset();
1090 return false;
1091 }
1092}
1093
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001094void SkCanvas::onPushCull(const SkRect& cullRect) {
1095 // do nothing. Subclasses may do something
1096}
1097
1098void SkCanvas::onPopCull() {
1099 // do nothing. Subclasses may do something
1100}
1101
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102/////////////////////////////////////////////////////////////////////////////
1103
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001104void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001106 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 return;
1108 }
1109
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001110 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001112 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001114
1115 SkDEBUGCODE(bitmap.validate();)
1116 CHECK_LOCKCOUNT_BALANCE(bitmap);
1117
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001118 SkRect storage;
1119 const SkRect* bounds = NULL;
1120 if (paint && paint->canComputeFastBounds()) {
1121 bitmap.getBounds(&storage);
1122 matrix.mapRect(&storage);
1123 bounds = &paint->computeFastBounds(storage, &storage);
1124 }
1125
1126 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001127
1128 while (iter.next()) {
1129 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1130 }
1131
1132 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133}
1134
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001135void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001136 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 SkPaint tmp;
1138 if (NULL == paint) {
1139 tmp.setDither(true);
1140 paint = &tmp;
1141 }
reed@google.com4b226022011-01-11 18:32:13 +00001142
reed@google.com8926b162012-03-23 15:36:36 +00001143 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001145 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001146 paint = &looper.paint();
1147 SkImageFilter* filter = paint->getImageFilter();
1148 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001149 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001150 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001151 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001152 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001153 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001154 SkMatrix matrix = *iter.fMatrix;
1155 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001156 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001157 SkPaint tmpUnfiltered(*paint);
1158 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001159 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1160 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001161 }
1162 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001163 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001164 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001166 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167}
1168
reed@google.com8926b162012-03-23 15:36:36 +00001169void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1170 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001171 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001172 return;
1173 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001174 SkDEBUGCODE(bitmap.validate();)
1175 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001176
reed@google.com8926b162012-03-23 15:36:36 +00001177 SkPaint tmp;
1178 if (NULL == paint) {
1179 paint = &tmp;
1180 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001181
reed@google.com8926b162012-03-23 15:36:36 +00001182 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001183
reed@google.com8926b162012-03-23 15:36:36 +00001184 while (iter.next()) {
1185 paint = &looper.paint();
1186 SkImageFilter* filter = paint->getImageFilter();
1187 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1188 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001189 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001190 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001191 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001192 SkMatrix matrix = *iter.fMatrix;
1193 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001194 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001195 SkPaint tmpUnfiltered(*paint);
1196 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001197 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001198 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001199 }
1200 } else {
1201 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1202 }
1203 }
1204 LOOPER_END
1205}
1206
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207/////////////////////////////////////////////////////////////////////////////
1208
1209bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1210 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001211 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 return fMCRec->fMatrix->preTranslate(dx, dy);
1213}
1214
1215bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1216 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001217 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218 return fMCRec->fMatrix->preScale(sx, sy);
1219}
1220
1221bool SkCanvas::rotate(SkScalar degrees) {
1222 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001223 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 return fMCRec->fMatrix->preRotate(degrees);
1225}
1226
1227bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1228 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001229 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 return fMCRec->fMatrix->preSkew(sx, sy);
1231}
1232
1233bool SkCanvas::concat(const SkMatrix& matrix) {
1234 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001235 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 return fMCRec->fMatrix->preConcat(matrix);
1237}
1238
1239void SkCanvas::setMatrix(const SkMatrix& matrix) {
1240 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001241 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 *fMCRec->fMatrix = matrix;
1243}
1244
1245// this is not virtual, so it must call a virtual method so that subclasses
1246// will see its action
1247void SkCanvas::resetMatrix() {
1248 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001249
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 matrix.reset();
1251 this->setMatrix(matrix);
1252}
1253
1254//////////////////////////////////////////////////////////////////////////////
1255
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001256void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001257 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1258 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001259}
1260
1261void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001262#ifdef SK_ENABLE_CLIP_QUICKREJECT
1263 if (SkRegion::kIntersect_Op == op) {
1264 if (fMCRec->fRasterClip->isEmpty()) {
1265 return false;
1266 }
1267
reed@google.com3b3e8952012-08-16 20:53:31 +00001268 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001269 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001270 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001271
1272 fClipStack.clipEmpty();
1273 return fMCRec->fRasterClip->setEmpty();
1274 }
1275 }
1276#endif
1277
reed@google.com5c3d1472011-02-22 19:12:23 +00001278 AutoValidateClip avc(this);
1279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001281 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001282 if (!fAllowSoftClip) {
1283 edgeStyle = kHard_ClipEdgeStyle;
1284 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285
1286 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001287 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001288 // the matrix. This means we don't have to a) make a path, and b) tell
1289 // the region code to scan-convert the path, only to discover that it
1290 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292
1293 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001294 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1295 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001297 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001298 // and clip against that, since it can handle any matrix. However, to
1299 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1300 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 SkPath path;
1302
1303 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001304 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 }
1306}
1307
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001308static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1309 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001310 // base is used to limit the size (and therefore memory allocation) of the
1311 // region that results from scan converting devPath.
1312 SkRegion base;
1313
reed@google.com819c9212011-02-23 18:56:55 +00001314 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001315 // since we are intersect, we can do better (tighter) with currRgn's
1316 // bounds, than just using the device. However, if currRgn is complex,
1317 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001318 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001319 // FIXME: we should also be able to do this when currClip->isBW(),
1320 // but relaxing the test above triggers GM asserts in
1321 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001322 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001323 } else {
reed@google.com00177082011-10-12 14:34:30 +00001324 base.setRect(currClip->getBounds());
1325 SkRasterClip clip;
1326 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001327 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001328 }
reed@google.com819c9212011-02-23 18:56:55 +00001329 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001330 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001331 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001332 currClip->setEmpty();
1333 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001334 }
1335
junov@chromium.orga907ac32012-02-24 21:54:07 +00001336 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001337
1338 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001339 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001340 } else {
reed@google.com00177082011-10-12 14:34:30 +00001341 SkRasterClip clip;
1342 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001343 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001344 }
1345 }
1346}
1347
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001348void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001349 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001350 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001351 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1352 } else {
1353 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001354 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001355}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001356
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001357void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001358 SkRRect transformedRRect;
1359 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1360 AutoValidateClip avc(this);
1361
1362 fDeviceCMDirty = true;
1363 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001364 if (!fAllowSoftClip) {
1365 edgeStyle = kHard_ClipEdgeStyle;
1366 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001367
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001368 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001369
1370 SkPath devPath;
1371 devPath.addRRect(transformedRRect);
1372
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001373 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1374 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001375 }
1376
1377 SkPath path;
1378 path.addRRect(rrect);
1379 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001381}
1382
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001383void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001384 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1385 SkRect r;
1386 if (!path.isInverseFillType() && path.isRect(&r)) {
1387 this->onClipRect(r, op, edgeStyle);
1388 } else {
1389 this->onClipPath(path, op, edgeStyle);
1390 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001391}
1392
1393void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001394#ifdef SK_ENABLE_CLIP_QUICKREJECT
1395 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1396 if (fMCRec->fRasterClip->isEmpty()) {
1397 return false;
1398 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001399
reed@google.com3b3e8952012-08-16 20:53:31 +00001400 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001401 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001402 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001403
reed@google.comda17f752012-08-16 18:27:05 +00001404 fClipStack.clipEmpty();
1405 return fMCRec->fRasterClip->setEmpty();
1406 }
1407 }
1408#endif
1409
reed@google.com5c3d1472011-02-22 19:12:23 +00001410 AutoValidateClip avc(this);
1411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001413 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001414 if (!fAllowSoftClip) {
1415 edgeStyle = kHard_ClipEdgeStyle;
1416 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417
1418 SkPath devPath;
1419 path.transform(*fMCRec->fMatrix, &devPath);
1420
reed@google.comfe701122011-11-08 19:41:23 +00001421 // Check if the transfomation, or the original path itself
1422 // made us empty. Note this can also happen if we contained NaN
1423 // values. computing the bounds detects this, and will set our
1424 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1425 if (devPath.getBounds().isEmpty()) {
1426 // resetting the path will remove any NaN or other wanky values
1427 // that might upset our scan converter.
1428 devPath.reset();
1429 }
1430
reed@google.com5c3d1472011-02-22 19:12:23 +00001431 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001432 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001433
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001434 if (fAllowSimplifyClip) {
1435 devPath.reset();
1436 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1437 const SkClipStack* clipStack = getClipStack();
1438 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1439 const SkClipStack::Element* element;
1440 while ((element = iter.next())) {
1441 SkClipStack::Element::Type type = element->getType();
1442 if (type == SkClipStack::Element::kEmpty_Type) {
1443 continue;
1444 }
1445 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001446 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001447 SkRegion::Op elementOp = element->getOp();
1448 if (elementOp == SkRegion::kReplace_Op) {
1449 devPath = operand;
1450 } else {
1451 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1452 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001453 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1454 // perhaps we need an API change to avoid this sort of mixed-signals about
1455 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001456 if (element->isAA()) {
1457 edgeStyle = kSoft_ClipEdgeStyle;
1458 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001459 }
1460 op = SkRegion::kReplace_Op;
1461 }
1462
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001463 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464}
1465
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001466void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001467 bool inverseFilled) {
1468 // This is for updating the clip conservatively using only bounds
1469 // information.
1470 // Contract:
1471 // The current clip must contain the true clip. The true
1472 // clip is the clip that would have normally been computed
1473 // by calls to clipPath and clipRRect
1474 // Objective:
1475 // Keep the current clip as small as possible without
1476 // breaking the contract, using only clip bounding rectangles
1477 // (for performance).
1478
1479 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1480 // don't have to worry about getting caught in a loop. Thus anywhere
1481 // we call a virtual method, we explicitly prefix it with
1482 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001483
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001484 if (inverseFilled) {
1485 switch (op) {
1486 case SkRegion::kIntersect_Op:
1487 case SkRegion::kDifference_Op:
1488 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001489 // the clip unchanged conservatively respects the contract.
1490 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001491 case SkRegion::kUnion_Op:
1492 case SkRegion::kReplace_Op:
1493 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001494 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001495 // These ops can grow the current clip up to the extents of
1496 // the input clip, which is inverse filled, so we just set
1497 // the current clip to the device bounds.
1498 SkRect deviceBounds;
1499 SkIRect deviceIBounds;
1500 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001501 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001502 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1503 // set the clip in device space
1504 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001505 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001506 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001507 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 break;
1509 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001510 default:
1511 SkASSERT(0); // unhandled op?
1512 }
1513 } else {
1514 // Not inverse filled
1515 switch (op) {
1516 case SkRegion::kIntersect_Op:
1517 case SkRegion::kUnion_Op:
1518 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001519 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1520 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001521 case SkRegion::kDifference_Op:
1522 // Difference can only shrink the current clip.
1523 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001524 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001525 case SkRegion::kReverseDifference_Op:
1526 // To reverse, we swap in the bounds with a replace op.
1527 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1529 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001530 case SkRegion::kXOR_Op:
1531 // Be conservative, based on (A XOR B) always included in (A union B),
1532 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001533 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1534 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001535 default:
1536 SkASSERT(0); // unhandled op?
1537 }
1538 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001539}
1540
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001541void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001542 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001543}
1544
1545void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001546 AutoValidateClip avc(this);
1547
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001549 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550
reed@google.com5c3d1472011-02-22 19:12:23 +00001551 // todo: signal fClipStack that we have a region, and therefore (I guess)
1552 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001553 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001554
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001555 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556}
1557
reed@google.com819c9212011-02-23 18:56:55 +00001558#ifdef SK_DEBUG
1559void SkCanvas::validateClip() const {
1560 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001561 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001562 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001563 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001564 return;
1565 }
1566
reed@google.com819c9212011-02-23 18:56:55 +00001567 SkIRect ir;
1568 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001569 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001570
robertphillips@google.com80214e22012-07-20 15:33:18 +00001571 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001572 const SkClipStack::Element* element;
1573 while ((element = iter.next()) != NULL) {
1574 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001575 case SkClipStack::Element::kRect_Type:
1576 element->getRect().round(&ir);
1577 tmpClip.op(ir, element->getOp());
1578 break;
1579 case SkClipStack::Element::kEmpty_Type:
1580 tmpClip.setEmpty();
1581 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001582 default: {
1583 SkPath path;
1584 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001586 break;
1587 }
reed@google.com819c9212011-02-23 18:56:55 +00001588 }
1589 }
reed@google.com819c9212011-02-23 18:56:55 +00001590}
1591#endif
1592
reed@google.com90c07ea2012-04-13 13:50:27 +00001593void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001594 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001595 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001596
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001597 static const SkRect kEmpty = { 0, 0, 0, 0 };
1598 while ((element = iter.next()) != NULL) {
1599 switch (element->getType()) {
1600 case SkClipStack::Element::kPath_Type:
1601 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1602 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001603 case SkClipStack::Element::kRRect_Type:
1604 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1605 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001606 case SkClipStack::Element::kRect_Type:
1607 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1608 break;
1609 case SkClipStack::Element::kEmpty_Type:
1610 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1611 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001612 }
1613 }
1614}
1615
reed@google.com5c3d1472011-02-22 19:12:23 +00001616///////////////////////////////////////////////////////////////////////////////
1617
reed@google.com754de5f2014-02-24 19:38:20 +00001618bool SkCanvas::isClipEmpty() const {
1619 return fMCRec->fRasterClip->isEmpty();
1620}
1621
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001622bool SkCanvas::isClipRect() const {
1623 return fMCRec->fRasterClip->isRect();
1624}
1625
reed@google.com3b3e8952012-08-16 20:53:31 +00001626bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001627
reed@google.com16078632011-12-06 18:56:37 +00001628 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001629 return true;
1630
reed@google.com00177082011-10-12 14:34:30 +00001631 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 return true;
1633 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634
tomhudson@google.com8d430182011-06-06 19:11:19 +00001635 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001636 SkRect dst;
1637 fMCRec->fMatrix->mapRect(&dst, rect);
1638 SkIRect idst;
1639 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001640 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001641 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001642 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001643
reed@android.coma380ae42009-07-21 01:17:02 +00001644 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001645 // TODO: should we use | instead, or compare all 4 at once?
1646 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001647 return true;
1648 }
reed@google.comc0784db2013-12-13 21:16:12 +00001649 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001650 return true;
1651 }
1652 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654}
1655
reed@google.com3b3e8952012-08-16 20:53:31 +00001656bool SkCanvas::quickReject(const SkPath& path) const {
1657 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658}
1659
reed@google.com3b3e8952012-08-16 20:53:31 +00001660bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001661 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001662 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663 return false;
1664 }
1665
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001666 SkMatrix inverse;
1667 // if we can't invert the CTM, we can't return local clip bounds
1668 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001669 if (bounds) {
1670 bounds->setEmpty();
1671 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001672 return false;
1673 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001675 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001676 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001677 // adjust it outwards in case we are antialiasing
1678 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001679
reed@google.com8f4d2302013-12-17 16:44:46 +00001680 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1681 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682 inverse.mapRect(bounds, r);
1683 }
1684 return true;
1685}
1686
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001687bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001688 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001689 if (clip.isEmpty()) {
1690 if (bounds) {
1691 bounds->setEmpty();
1692 }
1693 return false;
1694 }
1695
1696 if (NULL != bounds) {
1697 *bounds = clip.getBounds();
1698 }
1699 return true;
1700}
1701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702const SkMatrix& SkCanvas::getTotalMatrix() const {
1703 return *fMCRec->fMatrix;
1704}
1705
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001706#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001707SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001708 if (fMCRec->fRasterClip->isEmpty()) {
1709 return kEmpty_ClipType;
1710 }
1711 if (fMCRec->fRasterClip->isRect()) {
1712 return kRect_ClipType;
1713 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001714 return kComplex_ClipType;
1715}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001716#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001717
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001718#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001720 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001722#endif
1723
1724const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1725 return fMCRec->fRasterClip->forceGetBW();
1726}
1727
1728void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1729 path->reset();
1730
1731 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1732 if (rgn.isEmpty()) {
1733 return;
1734 }
1735 (void)rgn.getBoundaryPath(path);
1736}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001738SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001739 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001740 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741}
1742
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001743GrContext* SkCanvas::getGrContext() {
1744#if SK_SUPPORT_GPU
1745 SkBaseDevice* device = this->getTopDevice();
1746 if (NULL != device) {
1747 GrRenderTarget* renderTarget = device->accessRenderTarget();
1748 if (NULL != renderTarget) {
1749 return renderTarget->getContext();
1750 }
1751 }
1752#endif
1753
1754 return NULL;
1755
1756}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001757
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001758void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1759 const SkPaint& paint) {
1760 if (outer.isEmpty()) {
1761 return;
1762 }
1763 if (inner.isEmpty()) {
1764 this->drawRRect(outer, paint);
1765 return;
1766 }
1767
1768 // We don't have this method (yet), but technically this is what we should
1769 // be able to assert...
1770 // SkASSERT(outer.contains(inner));
1771 //
1772 // For now at least check for containment of bounds
1773 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1774
1775 this->onDrawDRRect(outer, inner, paint);
1776}
1777
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778//////////////////////////////////////////////////////////////////////////////
1779// These are the virtual drawing methods
1780//////////////////////////////////////////////////////////////////////////////
1781
reed@google.com2a981812011-04-14 18:59:28 +00001782void SkCanvas::clear(SkColor color) {
1783 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001784 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001785 while (iter.next()) {
1786 iter.fDevice->clear(color);
1787 }
1788}
1789
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001791 this->internalDrawPaint(paint);
1792}
1793
1794void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001795 CHECK_SHADER_NOSETCONTEXT(paint);
1796
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001797 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798
1799 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001800 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801 }
1802
reed@google.com4e2b3d32011-04-07 14:18:59 +00001803 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001804}
1805
1806void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1807 const SkPaint& paint) {
1808 if ((long)count <= 0) {
1809 return;
1810 }
1811
reed@google.comea033602012-12-14 13:13:55 +00001812 CHECK_SHADER_NOSETCONTEXT(paint);
1813
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001814 SkRect r, storage;
1815 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001816 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001817 // special-case 2 points (common for drawing a single line)
1818 if (2 == count) {
1819 r.set(pts[0], pts[1]);
1820 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001821 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001822 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 bounds = &paint.computeFastStrokeBounds(r, &storage);
1824 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001825 return;
1826 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001827 }
reed@google.coma584aed2012-05-16 14:06:02 +00001828
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 SkASSERT(pts != NULL);
1830
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001831 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001832
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001834 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835 }
reed@google.com4b226022011-01-11 18:32:13 +00001836
reed@google.com4e2b3d32011-04-07 14:18:59 +00001837 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838}
1839
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001840void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001841 CHECK_SHADER_NOSETCONTEXT(paint);
1842
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001843 SkRect storage;
1844 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001846 bounds = &paint.computeFastBounds(r, &storage);
1847 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848 return;
1849 }
1850 }
reed@google.com4b226022011-01-11 18:32:13 +00001851
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001852 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853
1854 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001855 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856 }
1857
reed@google.com4e2b3d32011-04-07 14:18:59 +00001858 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001859}
1860
reed@google.com4ed0fb72012-12-12 20:48:18 +00001861void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001862 CHECK_SHADER_NOSETCONTEXT(paint);
1863
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001864 SkRect storage;
1865 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001866 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001867 bounds = &paint.computeFastBounds(oval, &storage);
1868 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001869 return;
1870 }
1871 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001872
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001873 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001874
1875 while (iter.next()) {
1876 iter.fDevice->drawOval(iter, oval, looper.paint());
1877 }
1878
1879 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001880}
1881
1882void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001883 CHECK_SHADER_NOSETCONTEXT(paint);
1884
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001885 SkRect storage;
1886 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001887 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001888 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1889 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001890 return;
1891 }
1892 }
1893
1894 if (rrect.isRect()) {
1895 // call the non-virtual version
1896 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001897 return;
1898 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001899 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001900 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1901 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001902 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001903
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001904 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001905
1906 while (iter.next()) {
1907 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1908 }
1909
1910 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001911}
1912
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001913void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1914 const SkPaint& paint) {
1915 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001916
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001917 SkRect storage;
1918 const SkRect* bounds = NULL;
1919 if (paint.canComputeFastBounds()) {
1920 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1921 if (this->quickReject(*bounds)) {
1922 return;
1923 }
1924 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001925
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001926 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001927
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001928 while (iter.next()) {
1929 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1930 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001931
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001932 LOOPER_END
1933}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001934
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001935void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001936 CHECK_SHADER_NOSETCONTEXT(paint);
1937
reed@google.com93645112012-07-26 16:11:47 +00001938 if (!path.isFinite()) {
1939 return;
1940 }
1941
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001942 SkRect storage;
1943 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001944 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001945 const SkRect& pathBounds = path.getBounds();
1946 bounds = &paint.computeFastBounds(pathBounds, &storage);
1947 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948 return;
1949 }
1950 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001951
1952 const SkRect& r = path.getBounds();
1953 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001954 if (path.isInverseFillType()) {
1955 this->internalDrawPaint(paint);
1956 }
1957 return;
1958 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001960 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001961
1962 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001963 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964 }
1965
reed@google.com4e2b3d32011-04-07 14:18:59 +00001966 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967}
1968
1969void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1970 const SkPaint* paint) {
1971 SkDEBUGCODE(bitmap.validate();)
1972
reed@google.com3d608122011-11-21 15:16:16 +00001973 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001974 SkRect bounds = {
1975 x, y,
1976 x + SkIntToScalar(bitmap.width()),
1977 y + SkIntToScalar(bitmap.height())
1978 };
1979 if (paint) {
1980 (void)paint->computeFastBounds(bounds, &bounds);
1981 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001982 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983 return;
1984 }
1985 }
reed@google.com4b226022011-01-11 18:32:13 +00001986
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987 SkMatrix matrix;
1988 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001989 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990}
1991
reed@google.com9987ec32011-09-07 11:57:52 +00001992// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001993void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001994 const SkRect& dst, const SkPaint* paint,
1995 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001996 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997 return;
1998 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001999
reed@google.comea033602012-12-14 13:13:55 +00002000 CHECK_LOCKCOUNT_BALANCE(bitmap);
2001
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002002 SkRect storage;
2003 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002004 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002005 if (paint) {
2006 bounds = &paint->computeFastBounds(dst, &storage);
2007 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002008 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002009 return;
2010 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011 }
reed@google.com3d608122011-11-21 15:16:16 +00002012
reed@google.com33535f32012-09-25 15:37:50 +00002013 SkLazyPaint lazy;
2014 if (NULL == paint) {
2015 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002017
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002018 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002019
reed@google.com33535f32012-09-25 15:37:50 +00002020 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002021 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002022 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002023
reed@google.com33535f32012-09-25 15:37:50 +00002024 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002025}
2026
reed@google.com71121732012-09-18 15:14:33 +00002027void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002028 const SkRect& dst, const SkPaint* paint,
2029 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002030 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002031 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002032}
2033
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2035 const SkPaint* paint) {
2036 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002037 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038}
2039
reed@google.com9987ec32011-09-07 11:57:52 +00002040void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2041 const SkIRect& center, const SkRect& dst,
2042 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002043 if (bitmap.drawsNothing()) {
2044 return;
2045 }
reed@google.com3d608122011-11-21 15:16:16 +00002046 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002047 SkRect storage;
2048 const SkRect* bounds = &dst;
2049 if (paint) {
2050 bounds = &paint->computeFastBounds(dst, &storage);
2051 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002052 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002053 return;
2054 }
2055 }
2056
reed@google.com9987ec32011-09-07 11:57:52 +00002057 const int32_t w = bitmap.width();
2058 const int32_t h = bitmap.height();
2059
2060 SkIRect c = center;
2061 // pin center to the bounds of the bitmap
2062 c.fLeft = SkMax32(0, center.fLeft);
2063 c.fTop = SkMax32(0, center.fTop);
2064 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2065 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2066
reed@google.com71121732012-09-18 15:14:33 +00002067 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002068 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002069 };
2070 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002071 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002072 };
reed@google.com9987ec32011-09-07 11:57:52 +00002073 SkScalar dstX[4] = {
2074 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2075 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2076 };
2077 SkScalar dstY[4] = {
2078 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2079 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2080 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002081
reed@google.com9987ec32011-09-07 11:57:52 +00002082 if (dstX[1] > dstX[2]) {
2083 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2084 dstX[2] = dstX[1];
2085 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002086
reed@google.com9987ec32011-09-07 11:57:52 +00002087 if (dstY[1] > dstY[2]) {
2088 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2089 dstY[2] = dstY[1];
2090 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002091
reed@google.com9987ec32011-09-07 11:57:52 +00002092 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002093 SkRect s, d;
2094
reed@google.com9987ec32011-09-07 11:57:52 +00002095 s.fTop = srcY[y];
2096 s.fBottom = srcY[y+1];
2097 d.fTop = dstY[y];
2098 d.fBottom = dstY[y+1];
2099 for (int x = 0; x < 3; x++) {
2100 s.fLeft = srcX[x];
2101 s.fRight = srcX[x+1];
2102 d.fLeft = dstX[x];
2103 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002104 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002105 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002106 }
2107 }
2108}
2109
2110void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2111 const SkRect& dst, const SkPaint* paint) {
2112 SkDEBUGCODE(bitmap.validate();)
2113
2114 // Need a device entry-point, so gpu can use a mesh
2115 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2116}
2117
reed@google.comf67e4cf2011-03-15 20:56:58 +00002118class SkDeviceFilteredPaint {
2119public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002120 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2121 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002122 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002123 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002124 newPaint->setFlags(flags.fFlags);
2125 newPaint->setHinting(flags.fHinting);
2126 fPaint = newPaint;
2127 } else {
2128 fPaint = &paint;
2129 }
2130 }
2131
reed@google.comf67e4cf2011-03-15 20:56:58 +00002132 const SkPaint& paint() const { return *fPaint; }
2133
2134private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002135 const SkPaint* fPaint;
2136 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002137};
2138
bungeman@google.com52c748b2011-08-22 21:30:43 +00002139void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2140 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002141 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002142 draw.fDevice->drawRect(draw, r, paint);
2143 } else {
2144 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002145 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002146 draw.fDevice->drawRect(draw, r, p);
2147 }
2148}
2149
2150void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2151 const char text[], size_t byteLength,
2152 SkScalar x, SkScalar y) {
2153 SkASSERT(byteLength == 0 || text != NULL);
2154
2155 // nothing to draw
2156 if (text == NULL || byteLength == 0 ||
2157 draw.fClip->isEmpty() ||
2158 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2159 return;
2160 }
2161
2162 SkScalar width = 0;
2163 SkPoint start;
2164
2165 start.set(0, 0); // to avoid warning
2166 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2167 SkPaint::kStrikeThruText_Flag)) {
2168 width = paint.measureText(text, byteLength);
2169
2170 SkScalar offsetX = 0;
2171 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2172 offsetX = SkScalarHalf(width);
2173 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2174 offsetX = width;
2175 }
2176 start.set(x - offsetX, y);
2177 }
2178
2179 if (0 == width) {
2180 return;
2181 }
2182
2183 uint32_t flags = paint.getFlags();
2184
2185 if (flags & (SkPaint::kUnderlineText_Flag |
2186 SkPaint::kStrikeThruText_Flag)) {
2187 SkScalar textSize = paint.getTextSize();
2188 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2189 SkRect r;
2190
2191 r.fLeft = start.fX;
2192 r.fRight = start.fX + width;
2193
2194 if (flags & SkPaint::kUnderlineText_Flag) {
2195 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2196 start.fY);
2197 r.fTop = offset;
2198 r.fBottom = offset + height;
2199 DrawRect(draw, paint, r, textSize);
2200 }
2201 if (flags & SkPaint::kStrikeThruText_Flag) {
2202 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2203 start.fY);
2204 r.fTop = offset;
2205 r.fBottom = offset + height;
2206 DrawRect(draw, paint, r, textSize);
2207 }
2208 }
2209}
2210
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211void SkCanvas::drawText(const void* text, size_t byteLength,
2212 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002213 CHECK_SHADER_NOSETCONTEXT(paint);
2214
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002215 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216
2217 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002218 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002219 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002220 DrawTextDecorations(iter, dfp.paint(),
2221 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 }
2223
reed@google.com4e2b3d32011-04-07 14:18:59 +00002224 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225}
2226
2227void SkCanvas::drawPosText(const void* text, size_t byteLength,
2228 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002229 CHECK_SHADER_NOSETCONTEXT(paint);
2230
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002231 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002232
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002234 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002236 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237 }
reed@google.com4b226022011-01-11 18:32:13 +00002238
reed@google.com4e2b3d32011-04-07 14:18:59 +00002239 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240}
2241
2242void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2243 const SkScalar xpos[], SkScalar constY,
2244 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002245 CHECK_SHADER_NOSETCONTEXT(paint);
2246
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002247 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002248
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002250 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002252 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253 }
reed@google.com4b226022011-01-11 18:32:13 +00002254
reed@google.com4e2b3d32011-04-07 14:18:59 +00002255 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256}
2257
2258void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2259 const SkPath& path, const SkMatrix* matrix,
2260 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002261 CHECK_SHADER_NOSETCONTEXT(paint);
2262
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002263 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264
2265 while (iter.next()) {
2266 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002267 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268 }
2269
reed@google.com4e2b3d32011-04-07 14:18:59 +00002270 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271}
2272
2273void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2274 const SkPoint verts[], const SkPoint texs[],
2275 const SkColor colors[], SkXfermode* xmode,
2276 const uint16_t indices[], int indexCount,
2277 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002278 CHECK_SHADER_NOSETCONTEXT(paint);
2279
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002280 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002281
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282 while (iter.next()) {
2283 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002284 colors, xmode, indices, indexCount,
2285 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 }
reed@google.com4b226022011-01-11 18:32:13 +00002287
reed@google.com4e2b3d32011-04-07 14:18:59 +00002288 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002289}
2290
2291//////////////////////////////////////////////////////////////////////////////
2292// These methods are NOT virtual, and therefore must call back into virtual
2293// methods, rather than actually drawing themselves.
2294//////////////////////////////////////////////////////////////////////////////
2295
2296void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002297 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002298 SkPaint paint;
2299
2300 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002301 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002302 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002303 }
2304 this->drawPaint(paint);
2305}
2306
reed@android.com845fdac2009-06-23 03:01:32 +00002307void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002308 SkPaint paint;
2309
2310 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002311 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002312 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313 }
2314 this->drawPaint(paint);
2315}
2316
2317void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2318 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002319
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 pt.set(x, y);
2321 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2322}
2323
2324void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2325 SkPoint pt;
2326 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002327
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328 pt.set(x, y);
2329 paint.setColor(color);
2330 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2331}
2332
2333void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2334 const SkPaint& paint) {
2335 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002336
reed@android.com8a1c16f2008-12-17 15:59:43 +00002337 pts[0].set(x0, y0);
2338 pts[1].set(x1, y1);
2339 this->drawPoints(kLines_PointMode, 2, pts, paint);
2340}
2341
2342void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2343 SkScalar right, SkScalar bottom,
2344 const SkPaint& paint) {
2345 SkRect r;
2346
2347 r.set(left, top, right, bottom);
2348 this->drawRect(r, paint);
2349}
2350
2351void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2352 const SkPaint& paint) {
2353 if (radius < 0) {
2354 radius = 0;
2355 }
2356
2357 SkRect r;
2358 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002359 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360}
2361
2362void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2363 const SkPaint& paint) {
2364 if (rx > 0 && ry > 0) {
2365 if (paint.canComputeFastBounds()) {
2366 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002367 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002368 return;
2369 }
2370 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002371 SkRRect rrect;
2372 rrect.setRectXY(r, rx, ry);
2373 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 } else {
2375 this->drawRect(r, paint);
2376 }
2377}
2378
reed@android.com8a1c16f2008-12-17 15:59:43 +00002379void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2380 SkScalar sweepAngle, bool useCenter,
2381 const SkPaint& paint) {
2382 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2383 this->drawOval(oval, paint);
2384 } else {
2385 SkPath path;
2386 if (useCenter) {
2387 path.moveTo(oval.centerX(), oval.centerY());
2388 }
2389 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2390 if (useCenter) {
2391 path.close();
2392 }
2393 this->drawPath(path, paint);
2394 }
2395}
2396
2397void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2398 const SkPath& path, SkScalar hOffset,
2399 SkScalar vOffset, const SkPaint& paint) {
2400 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002401
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402 matrix.setTranslate(hOffset, vOffset);
2403 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2404}
2405
reed@android.comf76bacf2009-05-13 14:00:33 +00002406///////////////////////////////////////////////////////////////////////////////
2407
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002410}
2411
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412///////////////////////////////////////////////////////////////////////////////
2413///////////////////////////////////////////////////////////////////////////////
2414
2415SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002416 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417
2418 SkASSERT(canvas);
2419
2420 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2421 fDone = !fImpl->next();
2422}
2423
2424SkCanvas::LayerIter::~LayerIter() {
2425 fImpl->~SkDrawIter();
2426}
2427
2428void SkCanvas::LayerIter::next() {
2429 fDone = !fImpl->next();
2430}
2431
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002432SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433 return fImpl->getDevice();
2434}
2435
2436const SkMatrix& SkCanvas::LayerIter::matrix() const {
2437 return fImpl->getMatrix();
2438}
2439
2440const SkPaint& SkCanvas::LayerIter::paint() const {
2441 const SkPaint* paint = fImpl->getPaint();
2442 if (NULL == paint) {
2443 paint = &fDefaultPaint;
2444 }
2445 return *paint;
2446}
2447
2448const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2449int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2450int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002451
2452///////////////////////////////////////////////////////////////////////////////
2453
2454SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002455
2456///////////////////////////////////////////////////////////////////////////////
2457
2458static bool supported_for_raster_canvas(const SkImageInfo& info) {
2459 switch (info.alphaType()) {
2460 case kPremul_SkAlphaType:
2461 case kOpaque_SkAlphaType:
2462 break;
2463 default:
2464 return false;
2465 }
2466
2467 switch (info.colorType()) {
2468 case kAlpha_8_SkColorType:
2469 case kRGB_565_SkColorType:
2470 case kPMColor_SkColorType:
2471 break;
2472 default:
2473 return false;
2474 }
2475
2476 return true;
2477}
2478
2479SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2480 if (!supported_for_raster_canvas(info)) {
2481 return NULL;
2482 }
2483
2484 SkBitmap bitmap;
2485 if (!bitmap.allocPixels(info)) {
2486 return NULL;
2487 }
2488
2489 // should this functionality be moved into allocPixels()?
2490 if (!bitmap.info().isOpaque()) {
2491 bitmap.eraseColor(0);
2492 }
2493 return SkNEW_ARGS(SkCanvas, (bitmap));
2494}