blob: e575aec98b210e813af61f9b11826c1dabf11411 [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());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000350 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
351 true, SkCanvas::kFullLayer_SaveLayerStrategy);
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
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000670bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
671 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
672 return false;
673 }
674
675 bool weAllocated = false;
676 if (NULL == bitmap->pixelRef()) {
677 if (!bitmap->allocPixels()) {
678 return false;
679 }
680 weAllocated = true;
681 }
682
683 SkBitmap bm(*bitmap);
684 bm.lockPixels();
685 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
686 return true;
687 }
688
689 if (weAllocated) {
690 bitmap->setPixelRef(NULL);
691 }
692 return false;
693}
reed@google.com51df9e32010-12-23 19:29:18 +0000694
bsalomon@google.comc6980972011-11-02 19:57:21 +0000695bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000696 SkIRect r = srcRect;
697 const SkISize size = this->getBaseLayerSize();
698 if (!r.intersect(0, 0, size.width(), size.height())) {
699 bitmap->reset();
700 return false;
701 }
702
703 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
704 // bitmap will already be reset.
705 return false;
706 }
707 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
708 bitmap->reset();
709 return false;
710 }
711 return true;
712}
713
714bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
715 switch (origInfo.colorType()) {
716 case kUnknown_SkColorType:
717 case kIndex_8_SkColorType:
718 return false;
719 default:
720 break;
721 }
722 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
723 return false;
724 }
725 if (0 == origInfo.width() || 0 == origInfo.height()) {
726 return false;
727 }
728
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000729 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000730 if (!device) {
731 return false;
732 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000733
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000734 const SkISize size = this->getBaseLayerSize();
735 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
736 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000737 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000738 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000739
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000740 SkImageInfo info = origInfo;
741 // the intersect may have shrunk info's logical size
742 info.fWidth = srcR.width();
743 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000744
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000745 // if x or y are negative, then we have to adjust pixels
746 if (x > 0) {
747 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000748 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000749 if (y > 0) {
750 y = 0;
751 }
752 // here x,y are either 0 or negative
753 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000754
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000755 // The device can assert that the requested area is always contained in its bounds
756 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000757}
758
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000759bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
760 if (bitmap.getTexture()) {
761 return false;
762 }
763 SkBitmap bm(bitmap);
764 bm.lockPixels();
765 if (bm.getPixels()) {
766 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
767 }
768 return false;
769}
770
771bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
772 int x, int y) {
773 switch (origInfo.colorType()) {
774 case kUnknown_SkColorType:
775 case kIndex_8_SkColorType:
776 return false;
777 default:
778 break;
779 }
780 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
781 return false;
782 }
783
784 const SkISize size = this->getBaseLayerSize();
785 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
786 if (!target.intersect(0, 0, size.width(), size.height())) {
787 return false;
788 }
789
790 SkBaseDevice* device = this->getDevice();
791 if (!device) {
792 return false;
793 }
794
795 SkImageInfo info = origInfo;
796 // the intersect may have shrunk info's logical size
797 info.fWidth = target.width();
798 info.fHeight = target.height();
799
800 // if x or y are negative, then we have to adjust pixels
801 if (x > 0) {
802 x = 0;
803 }
804 if (y > 0) {
805 y = 0;
806 }
807 // here x,y are either 0 or negative
808 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
809
810 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000811 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000812}
reed@google.com51df9e32010-12-23 19:29:18 +0000813
junov@google.com4370aed2012-01-18 16:21:08 +0000814SkCanvas* SkCanvas::canvasForDrawIter() {
815 return this;
816}
817
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818//////////////////////////////////////////////////////////////////////////////
819
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820void SkCanvas::updateDeviceCMCache() {
821 if (fDeviceCMDirty) {
822 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000823 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000827 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000829 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000831 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 } while ((layer = layer->fNext) != NULL);
833 }
834 fDeviceCMDirty = false;
835 }
836}
837
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838///////////////////////////////////////////////////////////////////////////////
839
840int SkCanvas::internalSave(SaveFlags flags) {
841 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000842
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 MCRec* newTop = (MCRec*)fMCStack.push_back();
844 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000845
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000847
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000848 if (SkCanvas::kClip_SaveFlag & flags) {
849 fClipStack.save();
850 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000851
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 return saveCount;
853}
854
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000855void SkCanvas::willSave(SaveFlags) {
856 // Do nothing. Subclasses may do something.
857}
858
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000860 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 // call shared impl
862 return this->internalSave(flags);
863}
864
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000866#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000868#else
869 return true;
870#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871}
872
junov@chromium.orga907ac32012-02-24 21:54:07 +0000873bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000874 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000875 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000876 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000877 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000878 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000879 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000880
881 if (imageFilter) {
882 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
883 // Filters may grow the bounds beyond the device bounds.
884 op = SkRegion::kReplace_Op;
885 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000886 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 if (NULL != bounds) {
888 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000889
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 this->getTotalMatrix().mapRect(&r, *bounds);
891 r.roundOut(&ir);
892 // early exit if the layer's bounds are clipped out
893 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000894 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000895 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000896 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000897 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 }
899 } else { // no user bounds, so just use the clip
900 ir = clipBounds;
901 }
902
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000903 if (bounds_affects_clip(flags)) {
904 fClipStack.clipDevRect(ir, op);
905 // early exit if the clip is now empty
906 if (!fMCRec->fRasterClip->op(ir, op)) {
907 return false;
908 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000909 }
910
911 if (intersection) {
912 *intersection = ir;
913 }
914 return true;
915}
916
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000917SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) {
918
919 // Do nothing. Subclasses may do something.
920 return kFullLayer_SaveLayerStrategy;
921}
922
junov@chromium.orga907ac32012-02-24 21:54:07 +0000923int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
924 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000925 // Overriding classes may return false to signal that we don't need to create a layer.
926 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
927 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000928}
929
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000930static SkBaseDevice* create_compatible_device(SkCanvas* canvas,
931 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000932 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000933 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000934}
935
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000936int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
937 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000938#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
939 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
940#endif
941
junov@chromium.orga907ac32012-02-24 21:54:07 +0000942 // do this before we create the layer. We don't call the public save() since
943 // that would invoke a possibly overridden virtual
944 int count = this->internalSave(flags);
945
946 fDeviceCMDirty = true;
947
948 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000949 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 return count;
951 }
952
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000953 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
954 // the clipRectBounds() call above?
955 if (kNoLayer_SaveLayerStrategy == strategy) {
956 return count;
957 }
958
reed@google.comb55deeb2012-01-06 14:43:09 +0000959 // Kill the imagefilter if our device doesn't allow it
960 SkLazyPaint lazyP;
961 if (paint && paint->getImageFilter()) {
962 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000963 if (justForImageFilter) {
964 // early exit if the layer was just for the imageFilter
965 return count;
966 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000967 SkPaint* p = lazyP.set(*paint);
968 p->setImageFilter(NULL);
969 paint = p;
970 }
971 }
972
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000973 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
974 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
975 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000977 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000978 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000979 device = create_compatible_device(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000980 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000981 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000982 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000983 if (NULL == device) {
984 SkDebugf("Unable to create device for layer.");
985 return count;
986 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000987
reed@google.com6f8f2922011-03-04 22:27:10 +0000988 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000989 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 device->unref();
991
992 layer->fNext = fMCRec->fTopLayer;
993 fMCRec->fLayer = layer;
994 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
995
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000996 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 return count;
998}
999
1000int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1001 SaveFlags flags) {
1002 if (0xFF == alpha) {
1003 return this->saveLayer(bounds, NULL, flags);
1004 } else {
1005 SkPaint tmpPaint;
1006 tmpPaint.setAlpha(alpha);
1007 return this->saveLayer(bounds, &tmpPaint, flags);
1008 }
1009}
1010
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001011void SkCanvas::willRestore() {
1012 // Do nothing. Subclasses may do something.
1013}
1014
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015void SkCanvas::restore() {
1016 // check for underflow
1017 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001018 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 this->internalRestore();
1020 }
1021}
1022
1023void SkCanvas::internalRestore() {
1024 SkASSERT(fMCStack.count() != 0);
1025
1026 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001027 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001029 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
1030 fClipStack.restore();
1031 }
1032
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001033 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 DeviceCM* layer = fMCRec->fLayer; // may be null
1035 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1036 fMCRec->fLayer = NULL;
1037
1038 // now do the normal restore()
1039 fMCRec->~MCRec(); // balanced in save()
1040 fMCStack.pop_back();
1041 fMCRec = (MCRec*)fMCStack.back();
1042
1043 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1044 since if we're being recorded, we don't want to record this (the
1045 recorder will have already recorded the restore).
1046 */
1047 if (NULL != layer) {
1048 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001049 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001050 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1051 layer->fPaint);
1052 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001054
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001055 SkASSERT(fSaveLayerCount > 0);
1056 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 }
1058 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001059 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060}
1061
1062int SkCanvas::getSaveCount() const {
1063 return fMCStack.count();
1064}
1065
1066void SkCanvas::restoreToCount(int count) {
1067 // sanity check
1068 if (count < 1) {
1069 count = 1;
1070 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001071
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001072 int n = this->getSaveCount() - count;
1073 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 this->restore();
1075 }
1076}
1077
reed@google.com7c202932011-12-14 18:48:05 +00001078bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001079 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001080}
1081
reed@google.com76f10a32014-02-05 15:32:21 +00001082SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1083 return this->onNewSurface(info);
1084}
1085
1086SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1087 SkBaseDevice* dev = this->getDevice();
1088 return dev ? dev->newSurface(info) : NULL;
1089}
1090
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001091SkImageInfo SkCanvas::imageInfo() const {
1092 SkBaseDevice* dev = this->getDevice();
1093 if (dev) {
1094 return dev->imageInfo();
1095 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001096 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001097 }
1098}
1099
1100const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1101 return this->onPeekPixels(info, rowBytes);
1102}
1103
1104const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1105 SkBaseDevice* dev = this->getDevice();
1106 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1107}
1108
reed@google.com9c135db2014-03-12 18:28:35 +00001109void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1110 return this->onAccessTopLayerPixels(info, rowBytes);
1111}
1112
1113void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1114 SkBaseDevice* dev = this->getTopDevice();
1115 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1116}
1117
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001118SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1119 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1120 if (NULL == fAddr) {
1121 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001122 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001123 return; // failure, fAddr is NULL
1124 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001125 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1126 return; // failure, fAddr is NULL
1127 }
1128 fAddr = fBitmap.getPixels();
1129 fRowBytes = fBitmap.rowBytes();
1130 }
1131 SkASSERT(fAddr); // success
1132}
1133
1134bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1135 if (fAddr) {
1136 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1137 NULL, NULL);
1138 } else {
1139 bitmap->reset();
1140 return false;
1141 }
1142}
1143
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001144void SkCanvas::onPushCull(const SkRect& cullRect) {
1145 // do nothing. Subclasses may do something
1146}
1147
1148void SkCanvas::onPopCull() {
1149 // do nothing. Subclasses may do something
1150}
1151
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001153#ifdef SK_DEBUG
1154// Ensure that cull rects are monotonically nested in device space.
1155void SkCanvas::validateCull(const SkIRect& devCull) {
1156 if (fCullStack.isEmpty()
1157 || devCull.isEmpty()
1158 || fCullStack.top().contains(devCull)) {
1159 return;
1160 }
1161
1162 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1163 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1164 fCullStack.top().x(), fCullStack.top().y(),
1165 fCullStack.top().right(), fCullStack.top().bottom()));
1166
1167#ifdef ASSERT_NESTED_CULLING
1168 SkDEBUGFAIL("Invalid cull.");
1169#endif
1170}
1171#endif
1172
1173void SkCanvas::pushCull(const SkRect& cullRect) {
1174 ++fCullCount;
1175 this->onPushCull(cullRect);
1176
1177#ifdef SK_DEBUG
1178 // Map the cull rect into device space.
1179 SkRect mappedCull;
1180 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1181
1182 // Take clipping into account.
1183 SkIRect devClip, devCull;
1184 mappedCull.roundOut(&devCull);
1185 this->getClipDeviceBounds(&devClip);
1186 if (!devCull.intersect(devClip)) {
1187 devCull.setEmpty();
1188 }
1189
1190 this->validateCull(devCull);
1191 fCullStack.push(devCull); // balanced in popCull
1192#endif
1193}
1194
1195void SkCanvas::popCull() {
1196 SkASSERT(fCullStack.count() == fCullCount);
1197
1198 if (fCullCount > 0) {
1199 --fCullCount;
1200 this->onPopCull();
1201
1202 SkDEBUGCODE(fCullStack.pop());
1203 }
1204}
1205
1206/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001208void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001210 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 return;
1212 }
1213
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001214 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001216 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001218
1219 SkDEBUGCODE(bitmap.validate();)
1220 CHECK_LOCKCOUNT_BALANCE(bitmap);
1221
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001222 SkRect storage;
1223 const SkRect* bounds = NULL;
1224 if (paint && paint->canComputeFastBounds()) {
1225 bitmap.getBounds(&storage);
1226 matrix.mapRect(&storage);
1227 bounds = &paint->computeFastBounds(storage, &storage);
1228 }
1229
1230 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001231
1232 while (iter.next()) {
1233 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1234 }
1235
1236 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237}
1238
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001239void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001240 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 SkPaint tmp;
1242 if (NULL == paint) {
1243 tmp.setDither(true);
1244 paint = &tmp;
1245 }
reed@google.com4b226022011-01-11 18:32:13 +00001246
reed@google.com8926b162012-03-23 15:36:36 +00001247 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001249 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001250 paint = &looper.paint();
1251 SkImageFilter* filter = paint->getImageFilter();
1252 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001253 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001254 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001255 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001256 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001257 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001258 SkMatrix matrix = *iter.fMatrix;
1259 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001260 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
1261 SkImageFilter::Context ctx(matrix, clipBounds);
1262 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001263 SkPaint tmpUnfiltered(*paint);
1264 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001265 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1266 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001267 }
1268 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001269 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001270 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001272 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273}
1274
reed@google.com8926b162012-03-23 15:36:36 +00001275void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1276 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001277 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001278 return;
1279 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001280 SkDEBUGCODE(bitmap.validate();)
1281 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001282
reed@google.com8926b162012-03-23 15:36:36 +00001283 SkPaint tmp;
1284 if (NULL == paint) {
1285 paint = &tmp;
1286 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001287
reed@google.com8926b162012-03-23 15:36:36 +00001288 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001289
reed@google.com8926b162012-03-23 15:36:36 +00001290 while (iter.next()) {
1291 paint = &looper.paint();
1292 SkImageFilter* filter = paint->getImageFilter();
1293 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1294 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001295 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001296 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001297 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001298 SkMatrix matrix = *iter.fMatrix;
1299 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001300 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1301 SkImageFilter::Context ctx(matrix, clipBounds);
1302 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001303 SkPaint tmpUnfiltered(*paint);
1304 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001305 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001306 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001307 }
1308 } else {
1309 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1310 }
1311 }
1312 LOOPER_END
1313}
1314
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001316void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001317 SkMatrix m;
1318 m.setTranslate(dx, dy);
1319 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320}
1321
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001322void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001323 SkMatrix m;
1324 m.setScale(sx, sy);
1325 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326}
1327
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001328void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001329 SkMatrix m;
1330 m.setRotate(degrees);
1331 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332}
1333
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001334void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001335 SkMatrix m;
1336 m.setSkew(sx, sy);
1337 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001338}
1339
1340void SkCanvas::didConcat(const SkMatrix&) {
1341 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342}
1343
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001344void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001345 if (matrix.isIdentity()) {
1346 return;
1347 }
1348
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001350 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001351 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001352
1353 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001354}
1355
1356void SkCanvas::didSetMatrix(const SkMatrix&) {
1357 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358}
1359
1360void SkCanvas::setMatrix(const SkMatrix& matrix) {
1361 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001362 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001364 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365}
1366
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367void SkCanvas::resetMatrix() {
1368 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 matrix.reset();
1371 this->setMatrix(matrix);
1372}
1373
1374//////////////////////////////////////////////////////////////////////////////
1375
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001376void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001377 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1378 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001379}
1380
1381void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001382#ifdef SK_ENABLE_CLIP_QUICKREJECT
1383 if (SkRegion::kIntersect_Op == op) {
1384 if (fMCRec->fRasterClip->isEmpty()) {
1385 return false;
1386 }
1387
reed@google.com3b3e8952012-08-16 20:53:31 +00001388 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001389 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001390 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001391
1392 fClipStack.clipEmpty();
1393 return fMCRec->fRasterClip->setEmpty();
1394 }
1395 }
1396#endif
1397
reed@google.com5c3d1472011-02-22 19:12:23 +00001398 AutoValidateClip avc(this);
1399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001401 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001402 if (!fAllowSoftClip) {
1403 edgeStyle = kHard_ClipEdgeStyle;
1404 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405
1406 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001407 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001408 // the matrix. This means we don't have to a) make a path, and b) tell
1409 // the region code to scan-convert the path, only to discover that it
1410 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
1413 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001414 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1415 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001417 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001418 // and clip against that, since it can handle any matrix. However, to
1419 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1420 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 SkPath path;
1422
1423 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001424 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 }
1426}
1427
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1429 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001430 // base is used to limit the size (and therefore memory allocation) of the
1431 // region that results from scan converting devPath.
1432 SkRegion base;
1433
reed@google.com819c9212011-02-23 18:56:55 +00001434 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001435 // since we are intersect, we can do better (tighter) with currRgn's
1436 // bounds, than just using the device. However, if currRgn is complex,
1437 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001438 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001439 // FIXME: we should also be able to do this when currClip->isBW(),
1440 // but relaxing the test above triggers GM asserts in
1441 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001442 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001443 } else {
reed@google.com00177082011-10-12 14:34:30 +00001444 base.setRect(currClip->getBounds());
1445 SkRasterClip clip;
1446 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001447 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001448 }
reed@google.com819c9212011-02-23 18:56:55 +00001449 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001450 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001451 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001452 currClip->setEmpty();
1453 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001454 }
1455
junov@chromium.orga907ac32012-02-24 21:54:07 +00001456 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001457
1458 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001459 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001460 } else {
reed@google.com00177082011-10-12 14:34:30 +00001461 SkRasterClip clip;
1462 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001463 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001464 }
1465 }
1466}
1467
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001468void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001469 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001470 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001471 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1472 } else {
1473 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001474 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001475}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001476
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001477void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001478 SkRRect transformedRRect;
1479 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1480 AutoValidateClip avc(this);
1481
1482 fDeviceCMDirty = true;
1483 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001484 if (!fAllowSoftClip) {
1485 edgeStyle = kHard_ClipEdgeStyle;
1486 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001487
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001488 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001489
1490 SkPath devPath;
1491 devPath.addRRect(transformedRRect);
1492
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001493 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1494 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001495 }
1496
1497 SkPath path;
1498 path.addRRect(rrect);
1499 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001500 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001501}
1502
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001503void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001504 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1505 SkRect r;
1506 if (!path.isInverseFillType() && path.isRect(&r)) {
1507 this->onClipRect(r, op, edgeStyle);
1508 } else {
1509 this->onClipPath(path, op, edgeStyle);
1510 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001511}
1512
1513void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001514#ifdef SK_ENABLE_CLIP_QUICKREJECT
1515 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1516 if (fMCRec->fRasterClip->isEmpty()) {
1517 return false;
1518 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001519
reed@google.com3b3e8952012-08-16 20:53:31 +00001520 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001521 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001522 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001523
reed@google.comda17f752012-08-16 18:27:05 +00001524 fClipStack.clipEmpty();
1525 return fMCRec->fRasterClip->setEmpty();
1526 }
1527 }
1528#endif
1529
reed@google.com5c3d1472011-02-22 19:12:23 +00001530 AutoValidateClip avc(this);
1531
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001533 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001534 if (!fAllowSoftClip) {
1535 edgeStyle = kHard_ClipEdgeStyle;
1536 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537
1538 SkPath devPath;
1539 path.transform(*fMCRec->fMatrix, &devPath);
1540
reed@google.comfe701122011-11-08 19:41:23 +00001541 // Check if the transfomation, or the original path itself
1542 // made us empty. Note this can also happen if we contained NaN
1543 // values. computing the bounds detects this, and will set our
1544 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1545 if (devPath.getBounds().isEmpty()) {
1546 // resetting the path will remove any NaN or other wanky values
1547 // that might upset our scan converter.
1548 devPath.reset();
1549 }
1550
reed@google.com5c3d1472011-02-22 19:12:23 +00001551 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001552 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001553
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001554 if (fAllowSimplifyClip) {
1555 devPath.reset();
1556 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1557 const SkClipStack* clipStack = getClipStack();
1558 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1559 const SkClipStack::Element* element;
1560 while ((element = iter.next())) {
1561 SkClipStack::Element::Type type = element->getType();
1562 if (type == SkClipStack::Element::kEmpty_Type) {
1563 continue;
1564 }
1565 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001566 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001567 SkRegion::Op elementOp = element->getOp();
1568 if (elementOp == SkRegion::kReplace_Op) {
1569 devPath = operand;
1570 } else {
1571 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1572 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001573 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1574 // perhaps we need an API change to avoid this sort of mixed-signals about
1575 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001576 if (element->isAA()) {
1577 edgeStyle = kSoft_ClipEdgeStyle;
1578 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001579 }
1580 op = SkRegion::kReplace_Op;
1581 }
1582
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584}
1585
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001586void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001587 bool inverseFilled) {
1588 // This is for updating the clip conservatively using only bounds
1589 // information.
1590 // Contract:
1591 // The current clip must contain the true clip. The true
1592 // clip is the clip that would have normally been computed
1593 // by calls to clipPath and clipRRect
1594 // Objective:
1595 // Keep the current clip as small as possible without
1596 // breaking the contract, using only clip bounding rectangles
1597 // (for performance).
1598
1599 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1600 // don't have to worry about getting caught in a loop. Thus anywhere
1601 // we call a virtual method, we explicitly prefix it with
1602 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001603
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001604 if (inverseFilled) {
1605 switch (op) {
1606 case SkRegion::kIntersect_Op:
1607 case SkRegion::kDifference_Op:
1608 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001609 // the clip unchanged conservatively respects the contract.
1610 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001611 case SkRegion::kUnion_Op:
1612 case SkRegion::kReplace_Op:
1613 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001615 // These ops can grow the current clip up to the extents of
1616 // the input clip, which is inverse filled, so we just set
1617 // the current clip to the device bounds.
1618 SkRect deviceBounds;
1619 SkIRect deviceIBounds;
1620 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001621 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001622 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1623 // set the clip in device space
1624 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001625 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001627 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001628 break;
1629 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001630 default:
1631 SkASSERT(0); // unhandled op?
1632 }
1633 } else {
1634 // Not inverse filled
1635 switch (op) {
1636 case SkRegion::kIntersect_Op:
1637 case SkRegion::kUnion_Op:
1638 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001639 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1640 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001641 case SkRegion::kDifference_Op:
1642 // Difference can only shrink the current clip.
1643 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001644 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001645 case SkRegion::kReverseDifference_Op:
1646 // To reverse, we swap in the bounds with a replace op.
1647 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1649 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001650 case SkRegion::kXOR_Op:
1651 // Be conservative, based on (A XOR B) always included in (A union B),
1652 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1654 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001655 default:
1656 SkASSERT(0); // unhandled op?
1657 }
1658 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001659}
1660
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001661void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001662 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001663}
1664
1665void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001666 AutoValidateClip avc(this);
1667
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001669 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670
reed@google.com5c3d1472011-02-22 19:12:23 +00001671 // todo: signal fClipStack that we have a region, and therefore (I guess)
1672 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001673 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001674
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001675 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676}
1677
reed@google.com819c9212011-02-23 18:56:55 +00001678#ifdef SK_DEBUG
1679void SkCanvas::validateClip() const {
1680 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001681 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001682 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001683 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001684 return;
1685 }
1686
reed@google.com819c9212011-02-23 18:56:55 +00001687 SkIRect ir;
1688 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001689 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001690
robertphillips@google.com80214e22012-07-20 15:33:18 +00001691 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001692 const SkClipStack::Element* element;
1693 while ((element = iter.next()) != NULL) {
1694 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001695 case SkClipStack::Element::kRect_Type:
1696 element->getRect().round(&ir);
1697 tmpClip.op(ir, element->getOp());
1698 break;
1699 case SkClipStack::Element::kEmpty_Type:
1700 tmpClip.setEmpty();
1701 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001702 default: {
1703 SkPath path;
1704 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001705 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001706 break;
1707 }
reed@google.com819c9212011-02-23 18:56:55 +00001708 }
1709 }
reed@google.com819c9212011-02-23 18:56:55 +00001710}
1711#endif
1712
reed@google.com90c07ea2012-04-13 13:50:27 +00001713void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001714 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001715 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001716
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001717 static const SkRect kEmpty = { 0, 0, 0, 0 };
1718 while ((element = iter.next()) != NULL) {
1719 switch (element->getType()) {
1720 case SkClipStack::Element::kPath_Type:
1721 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1722 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001723 case SkClipStack::Element::kRRect_Type:
1724 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1725 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001726 case SkClipStack::Element::kRect_Type:
1727 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1728 break;
1729 case SkClipStack::Element::kEmpty_Type:
1730 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1731 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001732 }
1733 }
1734}
1735
reed@google.com5c3d1472011-02-22 19:12:23 +00001736///////////////////////////////////////////////////////////////////////////////
1737
reed@google.com754de5f2014-02-24 19:38:20 +00001738bool SkCanvas::isClipEmpty() const {
1739 return fMCRec->fRasterClip->isEmpty();
1740}
1741
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001742bool SkCanvas::isClipRect() const {
1743 return fMCRec->fRasterClip->isRect();
1744}
1745
reed@google.com3b3e8952012-08-16 20:53:31 +00001746bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001747
reed@google.com16078632011-12-06 18:56:37 +00001748 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001749 return true;
1750
reed@google.com00177082011-10-12 14:34:30 +00001751 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752 return true;
1753 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754
tomhudson@google.com8d430182011-06-06 19:11:19 +00001755 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001756 SkRect dst;
1757 fMCRec->fMatrix->mapRect(&dst, rect);
1758 SkIRect idst;
1759 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001760 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001761 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001762 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001763
reed@android.coma380ae42009-07-21 01:17:02 +00001764 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001765 // TODO: should we use | instead, or compare all 4 at once?
1766 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001767 return true;
1768 }
reed@google.comc0784db2013-12-13 21:16:12 +00001769 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001770 return true;
1771 }
1772 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774}
1775
reed@google.com3b3e8952012-08-16 20:53:31 +00001776bool SkCanvas::quickReject(const SkPath& path) const {
1777 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778}
1779
reed@google.com3b3e8952012-08-16 20:53:31 +00001780bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001781 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001782 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 return false;
1784 }
1785
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001786 SkMatrix inverse;
1787 // if we can't invert the CTM, we can't return local clip bounds
1788 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001789 if (bounds) {
1790 bounds->setEmpty();
1791 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001792 return false;
1793 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001795 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001796 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001797 // adjust it outwards in case we are antialiasing
1798 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001799
reed@google.com8f4d2302013-12-17 16:44:46 +00001800 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1801 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 inverse.mapRect(bounds, r);
1803 }
1804 return true;
1805}
1806
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001807bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001808 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001809 if (clip.isEmpty()) {
1810 if (bounds) {
1811 bounds->setEmpty();
1812 }
1813 return false;
1814 }
1815
1816 if (NULL != bounds) {
1817 *bounds = clip.getBounds();
1818 }
1819 return true;
1820}
1821
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822const SkMatrix& SkCanvas::getTotalMatrix() const {
1823 return *fMCRec->fMatrix;
1824}
1825
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001826#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001827SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001828 if (fMCRec->fRasterClip->isEmpty()) {
1829 return kEmpty_ClipType;
1830 }
1831 if (fMCRec->fRasterClip->isRect()) {
1832 return kRect_ClipType;
1833 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001834 return kComplex_ClipType;
1835}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001836#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001837
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001838#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001840 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001842#endif
1843
1844const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1845 return fMCRec->fRasterClip->forceGetBW();
1846}
1847
1848void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1849 path->reset();
1850
1851 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1852 if (rgn.isEmpty()) {
1853 return;
1854 }
1855 (void)rgn.getBoundaryPath(path);
1856}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001857
reed@google.com9c135db2014-03-12 18:28:35 +00001858GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1859 SkBaseDevice* dev = this->getTopDevice();
1860 return dev ? dev->accessRenderTarget() : NULL;
1861}
1862
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001863SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001864 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001865 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866}
1867
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001868GrContext* SkCanvas::getGrContext() {
1869#if SK_SUPPORT_GPU
1870 SkBaseDevice* device = this->getTopDevice();
1871 if (NULL != device) {
1872 GrRenderTarget* renderTarget = device->accessRenderTarget();
1873 if (NULL != renderTarget) {
1874 return renderTarget->getContext();
1875 }
1876 }
1877#endif
1878
1879 return NULL;
1880
1881}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001882
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001883void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1884 const SkPaint& paint) {
1885 if (outer.isEmpty()) {
1886 return;
1887 }
1888 if (inner.isEmpty()) {
1889 this->drawRRect(outer, paint);
1890 return;
1891 }
1892
1893 // We don't have this method (yet), but technically this is what we should
1894 // be able to assert...
1895 // SkASSERT(outer.contains(inner));
1896 //
1897 // For now at least check for containment of bounds
1898 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1899
1900 this->onDrawDRRect(outer, inner, paint);
1901}
1902
reed@android.com8a1c16f2008-12-17 15:59:43 +00001903//////////////////////////////////////////////////////////////////////////////
1904// These are the virtual drawing methods
1905//////////////////////////////////////////////////////////////////////////////
1906
reed@google.com2a981812011-04-14 18:59:28 +00001907void SkCanvas::clear(SkColor color) {
1908 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001909 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001910 while (iter.next()) {
1911 iter.fDevice->clear(color);
1912 }
1913}
1914
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001915void SkCanvas::onDiscard() {
1916 if (NULL != fSurfaceBase) {
1917 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1918 }
1919}
1920
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001922 this->internalDrawPaint(paint);
1923}
1924
1925void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001926 CHECK_SHADER_NOSETCONTEXT(paint);
1927
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001928 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929
1930 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001931 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932 }
1933
reed@google.com4e2b3d32011-04-07 14:18:59 +00001934 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001935}
1936
1937void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1938 const SkPaint& paint) {
1939 if ((long)count <= 0) {
1940 return;
1941 }
1942
reed@google.comea033602012-12-14 13:13:55 +00001943 CHECK_SHADER_NOSETCONTEXT(paint);
1944
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001945 SkRect r, storage;
1946 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001947 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001948 // special-case 2 points (common for drawing a single line)
1949 if (2 == count) {
1950 r.set(pts[0], pts[1]);
1951 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001952 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001953 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001954 bounds = &paint.computeFastStrokeBounds(r, &storage);
1955 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001956 return;
1957 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001958 }
reed@google.coma584aed2012-05-16 14:06:02 +00001959
reed@android.com8a1c16f2008-12-17 15:59:43 +00001960 SkASSERT(pts != NULL);
1961
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001962 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001963
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001965 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 }
reed@google.com4b226022011-01-11 18:32:13 +00001967
reed@google.com4e2b3d32011-04-07 14:18:59 +00001968 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969}
1970
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001971void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001972 CHECK_SHADER_NOSETCONTEXT(paint);
1973
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001974 SkRect storage;
1975 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001977 bounds = &paint.computeFastBounds(r, &storage);
1978 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 return;
1980 }
1981 }
reed@google.com4b226022011-01-11 18:32:13 +00001982
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001983 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001984
1985 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001986 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987 }
1988
reed@google.com4e2b3d32011-04-07 14:18:59 +00001989 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990}
1991
reed@google.com4ed0fb72012-12-12 20:48:18 +00001992void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001993 CHECK_SHADER_NOSETCONTEXT(paint);
1994
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001995 SkRect storage;
1996 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001997 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001998 bounds = &paint.computeFastBounds(oval, &storage);
1999 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002000 return;
2001 }
2002 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002003
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002004 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002005
2006 while (iter.next()) {
2007 iter.fDevice->drawOval(iter, oval, looper.paint());
2008 }
2009
2010 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002011}
2012
2013void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002014 CHECK_SHADER_NOSETCONTEXT(paint);
2015
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002016 SkRect storage;
2017 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002018 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002019 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2020 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002021 return;
2022 }
2023 }
2024
2025 if (rrect.isRect()) {
2026 // call the non-virtual version
2027 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002028 return;
2029 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002030 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002031 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2032 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002033 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002034
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002035 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002036
2037 while (iter.next()) {
2038 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2039 }
2040
2041 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002042}
2043
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002044void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2045 const SkPaint& paint) {
2046 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002047
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002048 SkRect storage;
2049 const SkRect* bounds = NULL;
2050 if (paint.canComputeFastBounds()) {
2051 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2052 if (this->quickReject(*bounds)) {
2053 return;
2054 }
2055 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002056
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002057 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002058
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002059 while (iter.next()) {
2060 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2061 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002062
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002063 LOOPER_END
2064}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002065
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00002066void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002067 CHECK_SHADER_NOSETCONTEXT(paint);
2068
reed@google.com93645112012-07-26 16:11:47 +00002069 if (!path.isFinite()) {
2070 return;
2071 }
2072
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002073 SkRect storage;
2074 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002075 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002076 const SkRect& pathBounds = path.getBounds();
2077 bounds = &paint.computeFastBounds(pathBounds, &storage);
2078 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002079 return;
2080 }
2081 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002082
2083 const SkRect& r = path.getBounds();
2084 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002085 if (path.isInverseFillType()) {
2086 this->internalDrawPaint(paint);
2087 }
2088 return;
2089 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002091 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092
2093 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002094 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 }
2096
reed@google.com4e2b3d32011-04-07 14:18:59 +00002097 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002098}
2099
2100void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2101 const SkPaint* paint) {
2102 SkDEBUGCODE(bitmap.validate();)
2103
reed@google.com3d608122011-11-21 15:16:16 +00002104 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002105 SkRect bounds = {
2106 x, y,
2107 x + SkIntToScalar(bitmap.width()),
2108 y + SkIntToScalar(bitmap.height())
2109 };
2110 if (paint) {
2111 (void)paint->computeFastBounds(bounds, &bounds);
2112 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002113 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 return;
2115 }
2116 }
reed@google.com4b226022011-01-11 18:32:13 +00002117
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118 SkMatrix matrix;
2119 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002120 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002121}
2122
reed@google.com9987ec32011-09-07 11:57:52 +00002123// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002124void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002125 const SkRect& dst, const SkPaint* paint,
2126 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002127 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128 return;
2129 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002130
reed@google.comea033602012-12-14 13:13:55 +00002131 CHECK_LOCKCOUNT_BALANCE(bitmap);
2132
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002133 SkRect storage;
2134 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002135 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002136 if (paint) {
2137 bounds = &paint->computeFastBounds(dst, &storage);
2138 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002139 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002140 return;
2141 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 }
reed@google.com3d608122011-11-21 15:16:16 +00002143
reed@google.com33535f32012-09-25 15:37:50 +00002144 SkLazyPaint lazy;
2145 if (NULL == paint) {
2146 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002148
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002149 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002150
reed@google.com33535f32012-09-25 15:37:50 +00002151 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002152 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002153 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002154
reed@google.com33535f32012-09-25 15:37:50 +00002155 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156}
2157
reed@google.com71121732012-09-18 15:14:33 +00002158void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002159 const SkRect& dst, const SkPaint* paint,
2160 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002161 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002162 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002163}
2164
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2166 const SkPaint* paint) {
2167 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002168 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169}
2170
reed@google.com9987ec32011-09-07 11:57:52 +00002171void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2172 const SkIRect& center, const SkRect& dst,
2173 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002174 if (bitmap.drawsNothing()) {
2175 return;
2176 }
reed@google.com3d608122011-11-21 15:16:16 +00002177 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002178 SkRect storage;
2179 const SkRect* bounds = &dst;
2180 if (paint) {
2181 bounds = &paint->computeFastBounds(dst, &storage);
2182 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002183 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002184 return;
2185 }
2186 }
2187
reed@google.com9987ec32011-09-07 11:57:52 +00002188 const int32_t w = bitmap.width();
2189 const int32_t h = bitmap.height();
2190
2191 SkIRect c = center;
2192 // pin center to the bounds of the bitmap
2193 c.fLeft = SkMax32(0, center.fLeft);
2194 c.fTop = SkMax32(0, center.fTop);
2195 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2196 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2197
reed@google.com71121732012-09-18 15:14:33 +00002198 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002199 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002200 };
2201 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002202 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002203 };
reed@google.com9987ec32011-09-07 11:57:52 +00002204 SkScalar dstX[4] = {
2205 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2206 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2207 };
2208 SkScalar dstY[4] = {
2209 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2210 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2211 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002212
reed@google.com9987ec32011-09-07 11:57:52 +00002213 if (dstX[1] > dstX[2]) {
2214 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2215 dstX[2] = dstX[1];
2216 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002217
reed@google.com9987ec32011-09-07 11:57:52 +00002218 if (dstY[1] > dstY[2]) {
2219 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2220 dstY[2] = dstY[1];
2221 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002222
reed@google.com9987ec32011-09-07 11:57:52 +00002223 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002224 SkRect s, d;
2225
reed@google.com9987ec32011-09-07 11:57:52 +00002226 s.fTop = srcY[y];
2227 s.fBottom = srcY[y+1];
2228 d.fTop = dstY[y];
2229 d.fBottom = dstY[y+1];
2230 for (int x = 0; x < 3; x++) {
2231 s.fLeft = srcX[x];
2232 s.fRight = srcX[x+1];
2233 d.fLeft = dstX[x];
2234 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002235 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002236 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002237 }
2238 }
2239}
2240
2241void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2242 const SkRect& dst, const SkPaint* paint) {
2243 SkDEBUGCODE(bitmap.validate();)
2244
2245 // Need a device entry-point, so gpu can use a mesh
2246 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2247}
2248
reed@google.comf67e4cf2011-03-15 20:56:58 +00002249class SkDeviceFilteredPaint {
2250public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002251 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2252 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002253 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002254 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002255 newPaint->setFlags(flags.fFlags);
2256 newPaint->setHinting(flags.fHinting);
2257 fPaint = newPaint;
2258 } else {
2259 fPaint = &paint;
2260 }
2261 }
2262
reed@google.comf67e4cf2011-03-15 20:56:58 +00002263 const SkPaint& paint() const { return *fPaint; }
2264
2265private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002266 const SkPaint* fPaint;
2267 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002268};
2269
bungeman@google.com52c748b2011-08-22 21:30:43 +00002270void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2271 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002272 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002273 draw.fDevice->drawRect(draw, r, paint);
2274 } else {
2275 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002276 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002277 draw.fDevice->drawRect(draw, r, p);
2278 }
2279}
2280
2281void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2282 const char text[], size_t byteLength,
2283 SkScalar x, SkScalar y) {
2284 SkASSERT(byteLength == 0 || text != NULL);
2285
2286 // nothing to draw
2287 if (text == NULL || byteLength == 0 ||
2288 draw.fClip->isEmpty() ||
2289 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2290 return;
2291 }
2292
2293 SkScalar width = 0;
2294 SkPoint start;
2295
2296 start.set(0, 0); // to avoid warning
2297 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2298 SkPaint::kStrikeThruText_Flag)) {
2299 width = paint.measureText(text, byteLength);
2300
2301 SkScalar offsetX = 0;
2302 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2303 offsetX = SkScalarHalf(width);
2304 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2305 offsetX = width;
2306 }
2307 start.set(x - offsetX, y);
2308 }
2309
2310 if (0 == width) {
2311 return;
2312 }
2313
2314 uint32_t flags = paint.getFlags();
2315
2316 if (flags & (SkPaint::kUnderlineText_Flag |
2317 SkPaint::kStrikeThruText_Flag)) {
2318 SkScalar textSize = paint.getTextSize();
2319 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2320 SkRect r;
2321
2322 r.fLeft = start.fX;
2323 r.fRight = start.fX + width;
2324
2325 if (flags & SkPaint::kUnderlineText_Flag) {
2326 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2327 start.fY);
2328 r.fTop = offset;
2329 r.fBottom = offset + height;
2330 DrawRect(draw, paint, r, textSize);
2331 }
2332 if (flags & SkPaint::kStrikeThruText_Flag) {
2333 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2334 start.fY);
2335 r.fTop = offset;
2336 r.fBottom = offset + height;
2337 DrawRect(draw, paint, r, textSize);
2338 }
2339 }
2340}
2341
reed@android.com8a1c16f2008-12-17 15:59:43 +00002342void SkCanvas::drawText(const void* text, size_t byteLength,
2343 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002344 CHECK_SHADER_NOSETCONTEXT(paint);
2345
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002346 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002347
2348 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002349 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002350 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002351 DrawTextDecorations(iter, dfp.paint(),
2352 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353 }
2354
reed@google.com4e2b3d32011-04-07 14:18:59 +00002355 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356}
2357
2358void SkCanvas::drawPosText(const void* text, size_t byteLength,
2359 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002360 CHECK_SHADER_NOSETCONTEXT(paint);
2361
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002362 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002363
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002365 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002367 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002368 }
reed@google.com4b226022011-01-11 18:32:13 +00002369
reed@google.com4e2b3d32011-04-07 14:18:59 +00002370 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371}
2372
2373void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2374 const SkScalar xpos[], SkScalar constY,
2375 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002376 CHECK_SHADER_NOSETCONTEXT(paint);
2377
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002378 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002379
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002381 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002383 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384 }
reed@google.com4b226022011-01-11 18:32:13 +00002385
reed@google.com4e2b3d32011-04-07 14:18:59 +00002386 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387}
2388
2389void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2390 const SkPath& path, const SkMatrix* matrix,
2391 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002392 CHECK_SHADER_NOSETCONTEXT(paint);
2393
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002394 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395
2396 while (iter.next()) {
2397 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002398 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 }
2400
reed@google.com4e2b3d32011-04-07 14:18:59 +00002401 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402}
2403
2404void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2405 const SkPoint verts[], const SkPoint texs[],
2406 const SkColor colors[], SkXfermode* xmode,
2407 const uint16_t indices[], int indexCount,
2408 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002409 CHECK_SHADER_NOSETCONTEXT(paint);
2410
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002411 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002412
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413 while (iter.next()) {
2414 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002415 colors, xmode, indices, indexCount,
2416 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417 }
reed@google.com4b226022011-01-11 18:32:13 +00002418
reed@google.com4e2b3d32011-04-07 14:18:59 +00002419 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002420}
2421
2422//////////////////////////////////////////////////////////////////////////////
2423// These methods are NOT virtual, and therefore must call back into virtual
2424// methods, rather than actually drawing themselves.
2425//////////////////////////////////////////////////////////////////////////////
2426
2427void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002428 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002429 SkPaint paint;
2430
2431 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002432 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002433 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002434 }
2435 this->drawPaint(paint);
2436}
2437
reed@android.com845fdac2009-06-23 03:01:32 +00002438void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439 SkPaint paint;
2440
2441 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002442 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002443 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 }
2445 this->drawPaint(paint);
2446}
2447
2448void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2449 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002450
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451 pt.set(x, y);
2452 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2453}
2454
2455void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2456 SkPoint pt;
2457 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002458
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459 pt.set(x, y);
2460 paint.setColor(color);
2461 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2462}
2463
2464void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2465 const SkPaint& paint) {
2466 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002467
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468 pts[0].set(x0, y0);
2469 pts[1].set(x1, y1);
2470 this->drawPoints(kLines_PointMode, 2, pts, paint);
2471}
2472
2473void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2474 SkScalar right, SkScalar bottom,
2475 const SkPaint& paint) {
2476 SkRect r;
2477
2478 r.set(left, top, right, bottom);
2479 this->drawRect(r, paint);
2480}
2481
2482void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2483 const SkPaint& paint) {
2484 if (radius < 0) {
2485 radius = 0;
2486 }
2487
2488 SkRect r;
2489 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002490 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002491}
2492
2493void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2494 const SkPaint& paint) {
2495 if (rx > 0 && ry > 0) {
2496 if (paint.canComputeFastBounds()) {
2497 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002498 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002499 return;
2500 }
2501 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002502 SkRRect rrect;
2503 rrect.setRectXY(r, rx, ry);
2504 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 } else {
2506 this->drawRect(r, paint);
2507 }
2508}
2509
reed@android.com8a1c16f2008-12-17 15:59:43 +00002510void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2511 SkScalar sweepAngle, bool useCenter,
2512 const SkPaint& paint) {
2513 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2514 this->drawOval(oval, paint);
2515 } else {
2516 SkPath path;
2517 if (useCenter) {
2518 path.moveTo(oval.centerX(), oval.centerY());
2519 }
2520 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2521 if (useCenter) {
2522 path.close();
2523 }
2524 this->drawPath(path, paint);
2525 }
2526}
2527
2528void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2529 const SkPath& path, SkScalar hOffset,
2530 SkScalar vOffset, const SkPaint& paint) {
2531 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002532
reed@android.com8a1c16f2008-12-17 15:59:43 +00002533 matrix.setTranslate(hOffset, vOffset);
2534 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2535}
2536
reed@android.comf76bacf2009-05-13 14:00:33 +00002537///////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002538void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
2539 SkBaseDevice* device = this->getDevice();
2540 if (NULL != device) {
2541 device->EXPERIMENTAL_optimize(picture);
2542 }
2543}
reed@android.comf76bacf2009-05-13 14:00:33 +00002544
reed@android.com8a1c16f2008-12-17 15:59:43 +00002545void SkCanvas::drawPicture(SkPicture& picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002546 SkBaseDevice* device = this->getTopDevice();
2547 if (NULL != device) {
2548 // Canvas has to first give the device the opportunity to render
2549 // the picture itself.
2550 if (device->EXPERIMENTAL_drawPicture(picture)) {
2551 return; // the device has rendered the entire picture
2552 }
2553 }
2554
reed@android.com8a1c16f2008-12-17 15:59:43 +00002555 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002556}
2557
reed@android.com8a1c16f2008-12-17 15:59:43 +00002558///////////////////////////////////////////////////////////////////////////////
2559///////////////////////////////////////////////////////////////////////////////
2560
2561SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002562 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002563
2564 SkASSERT(canvas);
2565
2566 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2567 fDone = !fImpl->next();
2568}
2569
2570SkCanvas::LayerIter::~LayerIter() {
2571 fImpl->~SkDrawIter();
2572}
2573
2574void SkCanvas::LayerIter::next() {
2575 fDone = !fImpl->next();
2576}
2577
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002578SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002579 return fImpl->getDevice();
2580}
2581
2582const SkMatrix& SkCanvas::LayerIter::matrix() const {
2583 return fImpl->getMatrix();
2584}
2585
2586const SkPaint& SkCanvas::LayerIter::paint() const {
2587 const SkPaint* paint = fImpl->getPaint();
2588 if (NULL == paint) {
2589 paint = &fDefaultPaint;
2590 }
2591 return *paint;
2592}
2593
2594const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2595int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2596int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002597
2598///////////////////////////////////////////////////////////////////////////////
2599
2600SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002601
2602///////////////////////////////////////////////////////////////////////////////
2603
2604static bool supported_for_raster_canvas(const SkImageInfo& info) {
2605 switch (info.alphaType()) {
2606 case kPremul_SkAlphaType:
2607 case kOpaque_SkAlphaType:
2608 break;
2609 default:
2610 return false;
2611 }
2612
2613 switch (info.colorType()) {
2614 case kAlpha_8_SkColorType:
2615 case kRGB_565_SkColorType:
2616 case kPMColor_SkColorType:
2617 break;
2618 default:
2619 return false;
2620 }
2621
2622 return true;
2623}
2624
2625SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2626 if (!supported_for_raster_canvas(info)) {
2627 return NULL;
2628 }
2629
2630 SkBitmap bitmap;
2631 if (!bitmap.allocPixels(info)) {
2632 return NULL;
2633 }
2634
2635 // should this functionality be moved into allocPixels()?
2636 if (!bitmap.info().isOpaque()) {
2637 bitmap.eraseColor(0);
2638 }
2639 return SkNEW_ARGS(SkCanvas, (bitmap));
2640}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002641
2642SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2643 if (!supported_for_raster_canvas(info)) {
2644 return NULL;
2645 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002646
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002647 SkBitmap bitmap;
2648 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2649 return NULL;
2650 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002651
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002652 // should this functionality be moved into allocPixels()?
2653 if (!bitmap.info().isOpaque()) {
2654 bitmap.eraseColor(0);
2655 }
2656 return SkNEW_ARGS(SkCanvas, (bitmap));
2657}