blob: d8399717fe353908a5e364047b64a4cd08362b62 [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
commit-bot@chromium.org53783b02014-04-17 21:09:49 +000094class 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
reed@google.comea033602012-12-14 13:13:55 +0000114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
reed@google.comea033602012-12-14 13:13:55 +0000116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000119 #define CHECK_SHADER_NOSETCONTEXT(paint)
reed@google.comea033602012-12-14 13:13:55 +0000120#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
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001109void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1110 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1111 if (pixels && origin) {
1112 *origin = this->getTopDevice(false)->getOrigin();
1113 }
1114 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001115}
1116
1117void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1118 SkBaseDevice* dev = this->getTopDevice();
1119 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1120}
1121
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001122SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1123 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1124 if (NULL == fAddr) {
1125 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001126 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001127 return; // failure, fAddr is NULL
1128 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001129 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1130 return; // failure, fAddr is NULL
1131 }
1132 fAddr = fBitmap.getPixels();
1133 fRowBytes = fBitmap.rowBytes();
1134 }
1135 SkASSERT(fAddr); // success
1136}
1137
1138bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1139 if (fAddr) {
1140 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1141 NULL, NULL);
1142 } else {
1143 bitmap->reset();
1144 return false;
1145 }
1146}
1147
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001148void SkCanvas::onPushCull(const SkRect& cullRect) {
1149 // do nothing. Subclasses may do something
1150}
1151
1152void SkCanvas::onPopCull() {
1153 // do nothing. Subclasses may do something
1154}
1155
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001157#ifdef SK_DEBUG
1158// Ensure that cull rects are monotonically nested in device space.
1159void SkCanvas::validateCull(const SkIRect& devCull) {
1160 if (fCullStack.isEmpty()
1161 || devCull.isEmpty()
1162 || fCullStack.top().contains(devCull)) {
1163 return;
1164 }
1165
1166 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1167 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1168 fCullStack.top().x(), fCullStack.top().y(),
1169 fCullStack.top().right(), fCullStack.top().bottom()));
1170
1171#ifdef ASSERT_NESTED_CULLING
1172 SkDEBUGFAIL("Invalid cull.");
1173#endif
1174}
1175#endif
1176
1177void SkCanvas::pushCull(const SkRect& cullRect) {
1178 ++fCullCount;
1179 this->onPushCull(cullRect);
1180
1181#ifdef SK_DEBUG
1182 // Map the cull rect into device space.
1183 SkRect mappedCull;
1184 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1185
1186 // Take clipping into account.
1187 SkIRect devClip, devCull;
1188 mappedCull.roundOut(&devCull);
1189 this->getClipDeviceBounds(&devClip);
1190 if (!devCull.intersect(devClip)) {
1191 devCull.setEmpty();
1192 }
1193
1194 this->validateCull(devCull);
1195 fCullStack.push(devCull); // balanced in popCull
1196#endif
1197}
1198
1199void SkCanvas::popCull() {
1200 SkASSERT(fCullStack.count() == fCullCount);
1201
1202 if (fCullCount > 0) {
1203 --fCullCount;
1204 this->onPopCull();
1205
1206 SkDEBUGCODE(fCullStack.pop());
1207 }
1208}
1209
1210/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001212void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001214 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 return;
1216 }
1217
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001218 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001220 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001222
1223 SkDEBUGCODE(bitmap.validate();)
1224 CHECK_LOCKCOUNT_BALANCE(bitmap);
1225
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001226 SkRect storage;
1227 const SkRect* bounds = NULL;
1228 if (paint && paint->canComputeFastBounds()) {
1229 bitmap.getBounds(&storage);
1230 matrix.mapRect(&storage);
1231 bounds = &paint->computeFastBounds(storage, &storage);
1232 }
1233
1234 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001235
1236 while (iter.next()) {
1237 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1238 }
1239
1240 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241}
1242
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001243void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001244 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 SkPaint tmp;
1246 if (NULL == paint) {
1247 tmp.setDither(true);
1248 paint = &tmp;
1249 }
reed@google.com4b226022011-01-11 18:32:13 +00001250
reed@google.com8926b162012-03-23 15:36:36 +00001251 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001253 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001254 paint = &looper.paint();
1255 SkImageFilter* filter = paint->getImageFilter();
1256 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001257 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001258 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001259 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001260 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001261 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001262 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001263 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001264 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001265 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1266 SkAutoUnref aur(NULL);
1267 if (!cache) {
1268 cache = SkImageFilter::Cache::Create();
1269 aur.reset(cache);
1270 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001271 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001272 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001273 SkPaint tmpUnfiltered(*paint);
1274 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001275 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1276 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001277 }
1278 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001279 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001282 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283}
1284
reed@google.com8926b162012-03-23 15:36:36 +00001285void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1286 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001287 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001288 return;
1289 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001290 SkDEBUGCODE(bitmap.validate();)
1291 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001292
reed@google.com8926b162012-03-23 15:36:36 +00001293 SkPaint tmp;
1294 if (NULL == paint) {
1295 paint = &tmp;
1296 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001297
reed@google.com8926b162012-03-23 15:36:36 +00001298 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001299
reed@google.com8926b162012-03-23 15:36:36 +00001300 while (iter.next()) {
1301 paint = &looper.paint();
1302 SkImageFilter* filter = paint->getImageFilter();
1303 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1304 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001305 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001306 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001307 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001308 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001309 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001310 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001311 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1312 SkAutoUnref aur(NULL);
1313 if (!cache) {
1314 cache = SkImageFilter::Cache::Create();
1315 aur.reset(cache);
1316 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001317 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001318 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001319 SkPaint tmpUnfiltered(*paint);
1320 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001321 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001322 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001323 }
1324 } else {
1325 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1326 }
1327 }
1328 LOOPER_END
1329}
1330
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001332void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001333 SkMatrix m;
1334 m.setTranslate(dx, dy);
1335 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336}
1337
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001338void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001339 SkMatrix m;
1340 m.setScale(sx, sy);
1341 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342}
1343
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001344void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001345 SkMatrix m;
1346 m.setRotate(degrees);
1347 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348}
1349
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001350void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001351 SkMatrix m;
1352 m.setSkew(sx, sy);
1353 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001354}
1355
1356void SkCanvas::didConcat(const SkMatrix&) {
1357 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358}
1359
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001360void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001361 if (matrix.isIdentity()) {
1362 return;
1363 }
1364
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001366 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001367 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001368
1369 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001370}
1371
1372void SkCanvas::didSetMatrix(const SkMatrix&) {
1373 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374}
1375
1376void SkCanvas::setMatrix(const SkMatrix& matrix) {
1377 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001378 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001380 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381}
1382
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383void SkCanvas::resetMatrix() {
1384 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001385
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 matrix.reset();
1387 this->setMatrix(matrix);
1388}
1389
1390//////////////////////////////////////////////////////////////////////////////
1391
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001392void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001393 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1394 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001395}
1396
1397void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001398#ifdef SK_ENABLE_CLIP_QUICKREJECT
1399 if (SkRegion::kIntersect_Op == op) {
1400 if (fMCRec->fRasterClip->isEmpty()) {
1401 return false;
1402 }
1403
reed@google.com3b3e8952012-08-16 20:53:31 +00001404 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001405 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001406 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001407
1408 fClipStack.clipEmpty();
1409 return fMCRec->fRasterClip->setEmpty();
1410 }
1411 }
1412#endif
1413
reed@google.com5c3d1472011-02-22 19:12:23 +00001414 AutoValidateClip avc(this);
1415
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001417 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001418 if (!fAllowSoftClip) {
1419 edgeStyle = kHard_ClipEdgeStyle;
1420 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421
1422 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001423 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001424 // the matrix. This means we don't have to a) make a path, and b) tell
1425 // the region code to scan-convert the path, only to discover that it
1426 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428
1429 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001430 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1431 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001433 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001434 // and clip against that, since it can handle any matrix. However, to
1435 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1436 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 SkPath path;
1438
1439 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001440 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 }
1442}
1443
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001444static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1445 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001446 // base is used to limit the size (and therefore memory allocation) of the
1447 // region that results from scan converting devPath.
1448 SkRegion base;
1449
reed@google.com819c9212011-02-23 18:56:55 +00001450 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001451 // since we are intersect, we can do better (tighter) with currRgn's
1452 // bounds, than just using the device. However, if currRgn is complex,
1453 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001454 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001455 // FIXME: we should also be able to do this when currClip->isBW(),
1456 // but relaxing the test above triggers GM asserts in
1457 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001458 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001459 } else {
reed@google.com00177082011-10-12 14:34:30 +00001460 base.setRect(currClip->getBounds());
1461 SkRasterClip clip;
1462 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001463 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001464 }
reed@google.com819c9212011-02-23 18:56:55 +00001465 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001466 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001467 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001468 currClip->setEmpty();
1469 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001470 }
1471
junov@chromium.orga907ac32012-02-24 21:54:07 +00001472 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001473
1474 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001475 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001476 } else {
reed@google.com00177082011-10-12 14:34:30 +00001477 SkRasterClip clip;
1478 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001479 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001480 }
1481 }
1482}
1483
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001484void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001485 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001486 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1488 } else {
1489 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001490 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001491}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001492
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001493void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001494 SkRRect transformedRRect;
1495 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1496 AutoValidateClip avc(this);
1497
1498 fDeviceCMDirty = true;
1499 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001500 if (!fAllowSoftClip) {
1501 edgeStyle = kHard_ClipEdgeStyle;
1502 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001503
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001504 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001505
1506 SkPath devPath;
1507 devPath.addRRect(transformedRRect);
1508
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1510 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001511 }
1512
1513 SkPath path;
1514 path.addRRect(rrect);
1515 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001516 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001517}
1518
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001519void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1521 SkRect r;
1522 if (!path.isInverseFillType() && path.isRect(&r)) {
1523 this->onClipRect(r, op, edgeStyle);
1524 } else {
1525 this->onClipPath(path, op, edgeStyle);
1526 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001527}
1528
1529void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001530#ifdef SK_ENABLE_CLIP_QUICKREJECT
1531 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1532 if (fMCRec->fRasterClip->isEmpty()) {
1533 return false;
1534 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001535
reed@google.com3b3e8952012-08-16 20:53:31 +00001536 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001537 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001538 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001539
reed@google.comda17f752012-08-16 18:27:05 +00001540 fClipStack.clipEmpty();
1541 return fMCRec->fRasterClip->setEmpty();
1542 }
1543 }
1544#endif
1545
reed@google.com5c3d1472011-02-22 19:12:23 +00001546 AutoValidateClip avc(this);
1547
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001549 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001550 if (!fAllowSoftClip) {
1551 edgeStyle = kHard_ClipEdgeStyle;
1552 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553
1554 SkPath devPath;
1555 path.transform(*fMCRec->fMatrix, &devPath);
1556
reed@google.comfe701122011-11-08 19:41:23 +00001557 // Check if the transfomation, or the original path itself
1558 // made us empty. Note this can also happen if we contained NaN
1559 // values. computing the bounds detects this, and will set our
1560 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1561 if (devPath.getBounds().isEmpty()) {
1562 // resetting the path will remove any NaN or other wanky values
1563 // that might upset our scan converter.
1564 devPath.reset();
1565 }
1566
reed@google.com5c3d1472011-02-22 19:12:23 +00001567 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001568 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001569
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001570 if (fAllowSimplifyClip) {
1571 devPath.reset();
1572 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1573 const SkClipStack* clipStack = getClipStack();
1574 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1575 const SkClipStack::Element* element;
1576 while ((element = iter.next())) {
1577 SkClipStack::Element::Type type = element->getType();
1578 if (type == SkClipStack::Element::kEmpty_Type) {
1579 continue;
1580 }
1581 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001582 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001583 SkRegion::Op elementOp = element->getOp();
1584 if (elementOp == SkRegion::kReplace_Op) {
1585 devPath = operand;
1586 } else {
1587 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1588 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001589 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1590 // perhaps we need an API change to avoid this sort of mixed-signals about
1591 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001592 if (element->isAA()) {
1593 edgeStyle = kSoft_ClipEdgeStyle;
1594 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001595 }
1596 op = SkRegion::kReplace_Op;
1597 }
1598
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001599 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600}
1601
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001602void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001603 bool inverseFilled) {
1604 // This is for updating the clip conservatively using only bounds
1605 // information.
1606 // Contract:
1607 // The current clip must contain the true clip. The true
1608 // clip is the clip that would have normally been computed
1609 // by calls to clipPath and clipRRect
1610 // Objective:
1611 // Keep the current clip as small as possible without
1612 // breaking the contract, using only clip bounding rectangles
1613 // (for performance).
1614
1615 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1616 // don't have to worry about getting caught in a loop. Thus anywhere
1617 // we call a virtual method, we explicitly prefix it with
1618 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001619
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001620 if (inverseFilled) {
1621 switch (op) {
1622 case SkRegion::kIntersect_Op:
1623 case SkRegion::kDifference_Op:
1624 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001625 // the clip unchanged conservatively respects the contract.
1626 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001627 case SkRegion::kUnion_Op:
1628 case SkRegion::kReplace_Op:
1629 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001630 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001631 // These ops can grow the current clip up to the extents of
1632 // the input clip, which is inverse filled, so we just set
1633 // the current clip to the device bounds.
1634 SkRect deviceBounds;
1635 SkIRect deviceIBounds;
1636 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001637 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001638
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001639 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001640 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001641 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001642 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001643 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001644 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001645 break;
1646 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001647 default:
1648 SkASSERT(0); // unhandled op?
1649 }
1650 } else {
1651 // Not inverse filled
1652 switch (op) {
1653 case SkRegion::kIntersect_Op:
1654 case SkRegion::kUnion_Op:
1655 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001656 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1657 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001658 case SkRegion::kDifference_Op:
1659 // Difference can only shrink the current clip.
1660 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001661 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001662 case SkRegion::kReverseDifference_Op:
1663 // To reverse, we swap in the bounds with a replace op.
1664 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001665 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1666 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001667 case SkRegion::kXOR_Op:
1668 // Be conservative, based on (A XOR B) always included in (A union B),
1669 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001670 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1671 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001672 default:
1673 SkASSERT(0); // unhandled op?
1674 }
1675 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001676}
1677
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001678void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001680}
1681
1682void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001683 AutoValidateClip avc(this);
1684
reed@android.com8a1c16f2008-12-17 15:59:43 +00001685 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001686 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687
reed@google.com5c3d1472011-02-22 19:12:23 +00001688 // todo: signal fClipStack that we have a region, and therefore (I guess)
1689 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001690 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001691
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001692 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693}
1694
reed@google.com819c9212011-02-23 18:56:55 +00001695#ifdef SK_DEBUG
1696void SkCanvas::validateClip() const {
1697 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001698 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001699 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001700 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001701 return;
1702 }
1703
reed@google.com819c9212011-02-23 18:56:55 +00001704 SkIRect ir;
1705 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001706 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001707
robertphillips@google.com80214e22012-07-20 15:33:18 +00001708 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001709 const SkClipStack::Element* element;
1710 while ((element = iter.next()) != NULL) {
1711 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001712 case SkClipStack::Element::kRect_Type:
1713 element->getRect().round(&ir);
1714 tmpClip.op(ir, element->getOp());
1715 break;
1716 case SkClipStack::Element::kEmpty_Type:
1717 tmpClip.setEmpty();
1718 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001719 default: {
1720 SkPath path;
1721 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001722 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001723 break;
1724 }
reed@google.com819c9212011-02-23 18:56:55 +00001725 }
1726 }
reed@google.com819c9212011-02-23 18:56:55 +00001727}
1728#endif
1729
reed@google.com90c07ea2012-04-13 13:50:27 +00001730void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001731 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001732 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001733
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001734 static const SkRect kEmpty = { 0, 0, 0, 0 };
1735 while ((element = iter.next()) != NULL) {
1736 switch (element->getType()) {
1737 case SkClipStack::Element::kPath_Type:
1738 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1739 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001740 case SkClipStack::Element::kRRect_Type:
1741 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1742 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001743 case SkClipStack::Element::kRect_Type:
1744 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1745 break;
1746 case SkClipStack::Element::kEmpty_Type:
1747 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1748 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001749 }
1750 }
1751}
1752
reed@google.com5c3d1472011-02-22 19:12:23 +00001753///////////////////////////////////////////////////////////////////////////////
1754
reed@google.com754de5f2014-02-24 19:38:20 +00001755bool SkCanvas::isClipEmpty() const {
1756 return fMCRec->fRasterClip->isEmpty();
1757}
1758
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001759bool SkCanvas::isClipRect() const {
1760 return fMCRec->fRasterClip->isRect();
1761}
1762
reed@google.com3b3e8952012-08-16 20:53:31 +00001763bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001764
reed@google.com16078632011-12-06 18:56:37 +00001765 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001766 return true;
1767
reed@google.com00177082011-10-12 14:34:30 +00001768 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769 return true;
1770 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771
tomhudson@google.com8d430182011-06-06 19:11:19 +00001772 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001773 SkRect dst;
1774 fMCRec->fMatrix->mapRect(&dst, rect);
1775 SkIRect idst;
1776 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001777 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001778 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001779 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001780
reed@android.coma380ae42009-07-21 01:17:02 +00001781 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001782 // TODO: should we use | instead, or compare all 4 at once?
1783 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001784 return true;
1785 }
reed@google.comc0784db2013-12-13 21:16:12 +00001786 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001787 return true;
1788 }
1789 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791}
1792
reed@google.com3b3e8952012-08-16 20:53:31 +00001793bool SkCanvas::quickReject(const SkPath& path) const {
1794 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795}
1796
reed@google.com3b3e8952012-08-16 20:53:31 +00001797bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001798 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001799 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 return false;
1801 }
1802
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001803 SkMatrix inverse;
1804 // if we can't invert the CTM, we can't return local clip bounds
1805 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001806 if (bounds) {
1807 bounds->setEmpty();
1808 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001809 return false;
1810 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001812 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001813 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001814 // adjust it outwards in case we are antialiasing
1815 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001816
reed@google.com8f4d2302013-12-17 16:44:46 +00001817 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1818 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819 inverse.mapRect(bounds, r);
1820 }
1821 return true;
1822}
1823
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001824bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001825 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001826 if (clip.isEmpty()) {
1827 if (bounds) {
1828 bounds->setEmpty();
1829 }
1830 return false;
1831 }
1832
1833 if (NULL != bounds) {
1834 *bounds = clip.getBounds();
1835 }
1836 return true;
1837}
1838
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839const SkMatrix& SkCanvas::getTotalMatrix() const {
1840 return *fMCRec->fMatrix;
1841}
1842
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001843#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001844SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001845 if (fMCRec->fRasterClip->isEmpty()) {
1846 return kEmpty_ClipType;
1847 }
1848 if (fMCRec->fRasterClip->isRect()) {
1849 return kRect_ClipType;
1850 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001851 return kComplex_ClipType;
1852}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001853#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001854
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001855#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001857 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001859#endif
1860
1861const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1862 return fMCRec->fRasterClip->forceGetBW();
1863}
1864
1865void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1866 path->reset();
1867
1868 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1869 if (rgn.isEmpty()) {
1870 return;
1871 }
1872 (void)rgn.getBoundaryPath(path);
1873}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874
reed@google.com9c135db2014-03-12 18:28:35 +00001875GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1876 SkBaseDevice* dev = this->getTopDevice();
1877 return dev ? dev->accessRenderTarget() : NULL;
1878}
1879
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001880SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001881 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001882 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883}
1884
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001885GrContext* SkCanvas::getGrContext() {
1886#if SK_SUPPORT_GPU
1887 SkBaseDevice* device = this->getTopDevice();
1888 if (NULL != device) {
1889 GrRenderTarget* renderTarget = device->accessRenderTarget();
1890 if (NULL != renderTarget) {
1891 return renderTarget->getContext();
1892 }
1893 }
1894#endif
1895
1896 return NULL;
1897
1898}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001899
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001900void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1901 const SkPaint& paint) {
1902 if (outer.isEmpty()) {
1903 return;
1904 }
1905 if (inner.isEmpty()) {
1906 this->drawRRect(outer, paint);
1907 return;
1908 }
1909
1910 // We don't have this method (yet), but technically this is what we should
1911 // be able to assert...
1912 // SkASSERT(outer.contains(inner));
1913 //
1914 // For now at least check for containment of bounds
1915 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1916
1917 this->onDrawDRRect(outer, inner, paint);
1918}
1919
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920//////////////////////////////////////////////////////////////////////////////
1921// These are the virtual drawing methods
1922//////////////////////////////////////////////////////////////////////////////
1923
reed@google.com2a981812011-04-14 18:59:28 +00001924void SkCanvas::clear(SkColor color) {
1925 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001926 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001927 while (iter.next()) {
1928 iter.fDevice->clear(color);
1929 }
1930}
1931
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001932void SkCanvas::onDiscard() {
1933 if (NULL != fSurfaceBase) {
1934 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1935 }
1936}
1937
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001939 this->internalDrawPaint(paint);
1940}
1941
1942void SkCanvas::internalDrawPaint(const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00001943 CHECK_SHADER_NOSETCONTEXT(paint);
1944
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001945 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946
1947 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001948 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949 }
1950
reed@google.com4e2b3d32011-04-07 14:18:59 +00001951 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952}
1953
1954void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1955 const SkPaint& paint) {
1956 if ((long)count <= 0) {
1957 return;
1958 }
1959
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00001960 CHECK_SHADER_NOSETCONTEXT(paint);
1961
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001962 SkRect r, storage;
1963 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001964 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001965 // special-case 2 points (common for drawing a single line)
1966 if (2 == count) {
1967 r.set(pts[0], pts[1]);
1968 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001969 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001970 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001971 bounds = &paint.computeFastStrokeBounds(r, &storage);
1972 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001973 return;
1974 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001975 }
reed@google.coma584aed2012-05-16 14:06:02 +00001976
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977 SkASSERT(pts != NULL);
1978
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001979 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001980
reed@android.com8a1c16f2008-12-17 15:59:43 +00001981 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001982 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983 }
reed@google.com4b226022011-01-11 18:32:13 +00001984
reed@google.com4e2b3d32011-04-07 14:18:59 +00001985 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986}
1987
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001988void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00001989 CHECK_SHADER_NOSETCONTEXT(paint);
1990
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001991 SkRect storage;
1992 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001993 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001994 bounds = &paint.computeFastBounds(r, &storage);
1995 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 return;
1997 }
1998 }
reed@google.com4b226022011-01-11 18:32:13 +00001999
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002000 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001
2002 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002003 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004 }
2005
reed@google.com4e2b3d32011-04-07 14:18:59 +00002006 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007}
2008
reed@google.com4ed0fb72012-12-12 20:48:18 +00002009void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002010 CHECK_SHADER_NOSETCONTEXT(paint);
2011
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002012 SkRect storage;
2013 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002014 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002015 bounds = &paint.computeFastBounds(oval, &storage);
2016 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002017 return;
2018 }
2019 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002020
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002021 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002022
2023 while (iter.next()) {
2024 iter.fDevice->drawOval(iter, oval, looper.paint());
2025 }
2026
2027 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002028}
2029
2030void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002031 CHECK_SHADER_NOSETCONTEXT(paint);
2032
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002033 SkRect storage;
2034 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002035 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002036 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2037 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002038 return;
2039 }
2040 }
2041
2042 if (rrect.isRect()) {
2043 // call the non-virtual version
2044 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002045 return;
2046 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002047 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002048 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2049 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002050 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002051
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002052 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002053
2054 while (iter.next()) {
2055 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2056 }
2057
2058 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002059}
2060
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002061void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2062 const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002063 CHECK_SHADER_NOSETCONTEXT(paint);
2064
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002065 SkRect storage;
2066 const SkRect* bounds = NULL;
2067 if (paint.canComputeFastBounds()) {
2068 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2069 if (this->quickReject(*bounds)) {
2070 return;
2071 }
2072 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002073
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002074 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002075
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002076 while (iter.next()) {
2077 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2078 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002079
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002080 LOOPER_END
2081}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002082
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00002083void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002084 CHECK_SHADER_NOSETCONTEXT(paint);
2085
reed@google.com93645112012-07-26 16:11:47 +00002086 if (!path.isFinite()) {
2087 return;
2088 }
2089
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002090 SkRect storage;
2091 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002092 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002093 const SkRect& pathBounds = path.getBounds();
2094 bounds = &paint.computeFastBounds(pathBounds, &storage);
2095 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096 return;
2097 }
2098 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002099
2100 const SkRect& r = path.getBounds();
2101 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002102 if (path.isInverseFillType()) {
2103 this->internalDrawPaint(paint);
2104 }
2105 return;
2106 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002108 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109
2110 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002111 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 }
2113
reed@google.com4e2b3d32011-04-07 14:18:59 +00002114 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002115}
2116
2117void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2118 const SkPaint* paint) {
2119 SkDEBUGCODE(bitmap.validate();)
2120
reed@google.com3d608122011-11-21 15:16:16 +00002121 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002122 SkRect bounds = {
2123 x, y,
2124 x + SkIntToScalar(bitmap.width()),
2125 y + SkIntToScalar(bitmap.height())
2126 };
2127 if (paint) {
2128 (void)paint->computeFastBounds(bounds, &bounds);
2129 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002130 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131 return;
2132 }
2133 }
reed@google.com4b226022011-01-11 18:32:13 +00002134
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135 SkMatrix matrix;
2136 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002137 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002138}
2139
reed@google.com9987ec32011-09-07 11:57:52 +00002140// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002141void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002142 const SkRect& dst, const SkPaint* paint,
2143 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002144 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145 return;
2146 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002147
reed@google.comea033602012-12-14 13:13:55 +00002148 CHECK_LOCKCOUNT_BALANCE(bitmap);
2149
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002150 SkRect storage;
2151 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002152 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002153 if (paint) {
2154 bounds = &paint->computeFastBounds(dst, &storage);
2155 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002156 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002157 return;
2158 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 }
reed@google.com3d608122011-11-21 15:16:16 +00002160
reed@google.com33535f32012-09-25 15:37:50 +00002161 SkLazyPaint lazy;
2162 if (NULL == paint) {
2163 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002165
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002166 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002167
reed@google.com33535f32012-09-25 15:37:50 +00002168 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002169 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002170 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002171
reed@google.com33535f32012-09-25 15:37:50 +00002172 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173}
2174
reed@google.com71121732012-09-18 15:14:33 +00002175void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002176 const SkRect& dst, const SkPaint* paint,
2177 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002178 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002179 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002180}
2181
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2183 const SkPaint* paint) {
2184 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002185 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186}
2187
reed@google.com9987ec32011-09-07 11:57:52 +00002188void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2189 const SkIRect& center, const SkRect& dst,
2190 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002191 if (bitmap.drawsNothing()) {
2192 return;
2193 }
reed@google.com3d608122011-11-21 15:16:16 +00002194 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002195 SkRect storage;
2196 const SkRect* bounds = &dst;
2197 if (paint) {
2198 bounds = &paint->computeFastBounds(dst, &storage);
2199 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002200 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002201 return;
2202 }
2203 }
2204
reed@google.com9987ec32011-09-07 11:57:52 +00002205 const int32_t w = bitmap.width();
2206 const int32_t h = bitmap.height();
2207
2208 SkIRect c = center;
2209 // pin center to the bounds of the bitmap
2210 c.fLeft = SkMax32(0, center.fLeft);
2211 c.fTop = SkMax32(0, center.fTop);
2212 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2213 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2214
reed@google.com71121732012-09-18 15:14:33 +00002215 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002216 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002217 };
2218 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002219 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002220 };
reed@google.com9987ec32011-09-07 11:57:52 +00002221 SkScalar dstX[4] = {
2222 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2223 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2224 };
2225 SkScalar dstY[4] = {
2226 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2227 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2228 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002229
reed@google.com9987ec32011-09-07 11:57:52 +00002230 if (dstX[1] > dstX[2]) {
2231 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2232 dstX[2] = dstX[1];
2233 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002234
reed@google.com9987ec32011-09-07 11:57:52 +00002235 if (dstY[1] > dstY[2]) {
2236 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2237 dstY[2] = dstY[1];
2238 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002239
reed@google.com9987ec32011-09-07 11:57:52 +00002240 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002241 SkRect s, d;
2242
reed@google.com9987ec32011-09-07 11:57:52 +00002243 s.fTop = srcY[y];
2244 s.fBottom = srcY[y+1];
2245 d.fTop = dstY[y];
2246 d.fBottom = dstY[y+1];
2247 for (int x = 0; x < 3; x++) {
2248 s.fLeft = srcX[x];
2249 s.fRight = srcX[x+1];
2250 d.fLeft = dstX[x];
2251 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002252 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002253 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002254 }
2255 }
2256}
2257
2258void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2259 const SkRect& dst, const SkPaint* paint) {
2260 SkDEBUGCODE(bitmap.validate();)
2261
2262 // Need a device entry-point, so gpu can use a mesh
2263 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2264}
2265
reed@google.comf67e4cf2011-03-15 20:56:58 +00002266class SkDeviceFilteredPaint {
2267public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002268 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2269 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002270 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002271 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002272 newPaint->setFlags(flags.fFlags);
2273 newPaint->setHinting(flags.fHinting);
2274 fPaint = newPaint;
2275 } else {
2276 fPaint = &paint;
2277 }
2278 }
2279
reed@google.comf67e4cf2011-03-15 20:56:58 +00002280 const SkPaint& paint() const { return *fPaint; }
2281
2282private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002283 const SkPaint* fPaint;
2284 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002285};
2286
bungeman@google.com52c748b2011-08-22 21:30:43 +00002287void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2288 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002289 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002290 draw.fDevice->drawRect(draw, r, paint);
2291 } else {
2292 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002293 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002294 draw.fDevice->drawRect(draw, r, p);
2295 }
2296}
2297
2298void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2299 const char text[], size_t byteLength,
2300 SkScalar x, SkScalar y) {
2301 SkASSERT(byteLength == 0 || text != NULL);
2302
2303 // nothing to draw
2304 if (text == NULL || byteLength == 0 ||
2305 draw.fClip->isEmpty() ||
2306 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2307 return;
2308 }
2309
2310 SkScalar width = 0;
2311 SkPoint start;
2312
2313 start.set(0, 0); // to avoid warning
2314 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2315 SkPaint::kStrikeThruText_Flag)) {
2316 width = paint.measureText(text, byteLength);
2317
2318 SkScalar offsetX = 0;
2319 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2320 offsetX = SkScalarHalf(width);
2321 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2322 offsetX = width;
2323 }
2324 start.set(x - offsetX, y);
2325 }
2326
2327 if (0 == width) {
2328 return;
2329 }
2330
2331 uint32_t flags = paint.getFlags();
2332
2333 if (flags & (SkPaint::kUnderlineText_Flag |
2334 SkPaint::kStrikeThruText_Flag)) {
2335 SkScalar textSize = paint.getTextSize();
2336 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2337 SkRect r;
2338
2339 r.fLeft = start.fX;
2340 r.fRight = start.fX + width;
2341
2342 if (flags & SkPaint::kUnderlineText_Flag) {
2343 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2344 start.fY);
2345 r.fTop = offset;
2346 r.fBottom = offset + height;
2347 DrawRect(draw, paint, r, textSize);
2348 }
2349 if (flags & SkPaint::kStrikeThruText_Flag) {
2350 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2351 start.fY);
2352 r.fTop = offset;
2353 r.fBottom = offset + height;
2354 DrawRect(draw, paint, r, textSize);
2355 }
2356 }
2357}
2358
reed@google.come0d9ce82014-04-23 04:00:17 +00002359void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2360 const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002361 CHECK_SHADER_NOSETCONTEXT(paint);
2362
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002363 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364
2365 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002366 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002367 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002368 DrawTextDecorations(iter, dfp.paint(),
2369 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 }
2371
reed@google.com4e2b3d32011-04-07 14:18:59 +00002372 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373}
2374
reed@google.come0d9ce82014-04-23 04:00:17 +00002375void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2376 const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002377 CHECK_SHADER_NOSETCONTEXT(paint);
reed@google.come0d9ce82014-04-23 04:00:17 +00002378
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002379 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.come0d9ce82014-04-23 04:00:17 +00002380
reed@android.com8a1c16f2008-12-17 15:59:43 +00002381 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002382 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002384 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002386
reed@google.com4e2b3d32011-04-07 14:18:59 +00002387 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388}
2389
reed@google.come0d9ce82014-04-23 04:00:17 +00002390void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2391 SkScalar constY, const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002392 CHECK_SHADER_NOSETCONTEXT(paint);
reed@google.come0d9ce82014-04-23 04:00:17 +00002393
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002394 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.come0d9ce82014-04-23 04:00:17 +00002395
reed@android.com8a1c16f2008-12-17 15:59:43 +00002396 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002397 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002399 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002401
reed@google.com4e2b3d32011-04-07 14:18:59 +00002402 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002403}
2404
reed@google.come0d9ce82014-04-23 04:00:17 +00002405void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2406 const SkMatrix* matrix, const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002407 CHECK_SHADER_NOSETCONTEXT(paint);
reed@google.come0d9ce82014-04-23 04:00:17 +00002408
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002409 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.come0d9ce82014-04-23 04:00:17 +00002410
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411 while (iter.next()) {
2412 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002413 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002414 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002415
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002416 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002417}
2418
reed@google.come0d9ce82014-04-23 04:00:17 +00002419// These will become non-virtual, so they always call the (virtual) onDraw... method
2420void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2421 const SkPaint& paint) {
2422 this->onDrawText(text, byteLength, x, y, paint);
2423}
2424void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2425 const SkPaint& paint) {
2426 this->onDrawPosText(text, byteLength, pos, paint);
2427}
2428void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2429 SkScalar constY, const SkPaint& paint) {
2430 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2431}
2432void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2433 const SkMatrix* matrix, const SkPaint& paint) {
2434 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2435}
2436
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2438 const SkPoint verts[], const SkPoint texs[],
2439 const SkColor colors[], SkXfermode* xmode,
2440 const uint16_t indices[], int indexCount,
2441 const SkPaint& paint) {
commit-bot@chromium.org53783b02014-04-17 21:09:49 +00002442 CHECK_SHADER_NOSETCONTEXT(paint);
2443
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002444 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002445
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446 while (iter.next()) {
2447 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002448 colors, xmode, indices, indexCount,
2449 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450 }
reed@google.com4b226022011-01-11 18:32:13 +00002451
reed@google.com4e2b3d32011-04-07 14:18:59 +00002452 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453}
2454
2455//////////////////////////////////////////////////////////////////////////////
2456// These methods are NOT virtual, and therefore must call back into virtual
2457// methods, rather than actually drawing themselves.
2458//////////////////////////////////////////////////////////////////////////////
2459
2460void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002461 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462 SkPaint paint;
2463
2464 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002465 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002466 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002467 }
2468 this->drawPaint(paint);
2469}
2470
reed@android.com845fdac2009-06-23 03:01:32 +00002471void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002472 SkPaint paint;
2473
2474 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002475 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002476 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002477 }
2478 this->drawPaint(paint);
2479}
2480
2481void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2482 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002483
reed@android.com8a1c16f2008-12-17 15:59:43 +00002484 pt.set(x, y);
2485 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2486}
2487
2488void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2489 SkPoint pt;
2490 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002491
reed@android.com8a1c16f2008-12-17 15:59:43 +00002492 pt.set(x, y);
2493 paint.setColor(color);
2494 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2495}
2496
2497void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2498 const SkPaint& paint) {
2499 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002500
reed@android.com8a1c16f2008-12-17 15:59:43 +00002501 pts[0].set(x0, y0);
2502 pts[1].set(x1, y1);
2503 this->drawPoints(kLines_PointMode, 2, pts, paint);
2504}
2505
2506void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2507 SkScalar right, SkScalar bottom,
2508 const SkPaint& paint) {
2509 SkRect r;
2510
2511 r.set(left, top, right, bottom);
2512 this->drawRect(r, paint);
2513}
2514
2515void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2516 const SkPaint& paint) {
2517 if (radius < 0) {
2518 radius = 0;
2519 }
2520
2521 SkRect r;
2522 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002523 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002524}
2525
2526void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2527 const SkPaint& paint) {
2528 if (rx > 0 && ry > 0) {
2529 if (paint.canComputeFastBounds()) {
2530 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002531 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002532 return;
2533 }
2534 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002535 SkRRect rrect;
2536 rrect.setRectXY(r, rx, ry);
2537 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002538 } else {
2539 this->drawRect(r, paint);
2540 }
2541}
2542
reed@android.com8a1c16f2008-12-17 15:59:43 +00002543void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2544 SkScalar sweepAngle, bool useCenter,
2545 const SkPaint& paint) {
2546 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2547 this->drawOval(oval, paint);
2548 } else {
2549 SkPath path;
2550 if (useCenter) {
2551 path.moveTo(oval.centerX(), oval.centerY());
2552 }
2553 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2554 if (useCenter) {
2555 path.close();
2556 }
2557 this->drawPath(path, paint);
2558 }
2559}
2560
2561void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2562 const SkPath& path, SkScalar hOffset,
2563 SkScalar vOffset, const SkPaint& paint) {
2564 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002565
reed@android.com8a1c16f2008-12-17 15:59:43 +00002566 matrix.setTranslate(hOffset, vOffset);
2567 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2568}
2569
reed@android.comf76bacf2009-05-13 14:00:33 +00002570///////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002571void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
2572 SkBaseDevice* device = this->getDevice();
2573 if (NULL != device) {
2574 device->EXPERIMENTAL_optimize(picture);
2575 }
2576}
reed@android.comf76bacf2009-05-13 14:00:33 +00002577
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002578void SkCanvas::EXPERIMENTAL_purge(SkPicture* picture) {
2579 SkBaseDevice* device = this->getTopDevice();
2580 if (NULL != device) {
2581 device->EXPERIMENTAL_purge(picture);
2582 }
2583}
2584
reed@android.com8a1c16f2008-12-17 15:59:43 +00002585void SkCanvas::drawPicture(SkPicture& picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002586 SkBaseDevice* device = this->getTopDevice();
2587 if (NULL != device) {
2588 // Canvas has to first give the device the opportunity to render
2589 // the picture itself.
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002590 if (device->EXPERIMENTAL_drawPicture(this, &picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002591 return; // the device has rendered the entire picture
2592 }
2593 }
2594
reed@android.com8a1c16f2008-12-17 15:59:43 +00002595 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002596}
2597
reed@android.com8a1c16f2008-12-17 15:59:43 +00002598///////////////////////////////////////////////////////////////////////////////
2599///////////////////////////////////////////////////////////////////////////////
2600
2601SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002602 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002603
2604 SkASSERT(canvas);
2605
2606 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2607 fDone = !fImpl->next();
2608}
2609
2610SkCanvas::LayerIter::~LayerIter() {
2611 fImpl->~SkDrawIter();
2612}
2613
2614void SkCanvas::LayerIter::next() {
2615 fDone = !fImpl->next();
2616}
2617
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002618SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002619 return fImpl->getDevice();
2620}
2621
2622const SkMatrix& SkCanvas::LayerIter::matrix() const {
2623 return fImpl->getMatrix();
2624}
2625
2626const SkPaint& SkCanvas::LayerIter::paint() const {
2627 const SkPaint* paint = fImpl->getPaint();
2628 if (NULL == paint) {
2629 paint = &fDefaultPaint;
2630 }
2631 return *paint;
2632}
2633
2634const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2635int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2636int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002637
2638///////////////////////////////////////////////////////////////////////////////
2639
2640SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002641
2642///////////////////////////////////////////////////////////////////////////////
2643
2644static bool supported_for_raster_canvas(const SkImageInfo& info) {
2645 switch (info.alphaType()) {
2646 case kPremul_SkAlphaType:
2647 case kOpaque_SkAlphaType:
2648 break;
2649 default:
2650 return false;
2651 }
2652
2653 switch (info.colorType()) {
2654 case kAlpha_8_SkColorType:
2655 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002656 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002657 break;
2658 default:
2659 return false;
2660 }
2661
2662 return true;
2663}
2664
2665SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2666 if (!supported_for_raster_canvas(info)) {
2667 return NULL;
2668 }
2669
2670 SkBitmap bitmap;
2671 if (!bitmap.allocPixels(info)) {
2672 return NULL;
2673 }
2674
2675 // should this functionality be moved into allocPixels()?
2676 if (!bitmap.info().isOpaque()) {
2677 bitmap.eraseColor(0);
2678 }
2679 return SkNEW_ARGS(SkCanvas, (bitmap));
2680}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002681
2682SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2683 if (!supported_for_raster_canvas(info)) {
2684 return NULL;
2685 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002686
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002687 SkBitmap bitmap;
2688 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2689 return NULL;
2690 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002691 return SkNEW_ARGS(SkCanvas, (bitmap));
2692}