blob: 1bc5e2fa7856105873fc9bb6725f061677869181 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkBounder.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
94class AutoCheckNoSetContext {
95public:
96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
97 this->assertNoSetContext(fPaint);
98 }
99 ~AutoCheckNoSetContext() {
100 this->assertNoSetContext(fPaint);
101 }
102
103private:
104 const SkPaint& fPaint;
105
106 void assertNoSetContext(const SkPaint& paint) {
107 SkShader* s = paint.getShader();
108 if (s) {
109 SkASSERT(!s->setContextHasBeenCalled());
110 }
111 }
112};
113
114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
119 #define CHECK_SHADER_NOSETCONTEXT(paint)
120#endif
121
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000122typedef SkTLazy<SkPaint> SkLazyPaint;
123
reed@google.com97af1a62012-08-28 12:19:02 +0000124void SkCanvas::predrawNotify() {
125 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000127 }
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000132/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 The clip/matrix/proc are fields that reflect the top of the save/restore
134 stack. Whenever the canvas changes, it marks a dirty flag, and then before
135 these are used (assuming we're not on a layer) we rebuild these cache
136 values: they reflect the top of the save stack, but translated and clipped
137 by the device's XY offset and bitmap-bounds.
138*/
139struct DeviceCM {
140 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000141 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000142 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000146 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 : fNext(NULL) {
148 if (NULL != device) {
149 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000150 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@google.com4b226022011-01-11 18:32:13 +0000152 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000156 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000158 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fDevice->unref();
160 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000161 SkDELETE(fPaint);
162 }
reed@google.com4b226022011-01-11 18:32:13 +0000163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
165 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000166 int x = fDevice->getOrigin().x();
167 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 int width = fDevice->width();
169 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if ((x | y) == 0) {
172 fMatrix = &totalMatrix;
173 fClip = totalClip;
174 } else {
175 fMatrixStorage = totalMatrix;
176 fMatrixStorage.postTranslate(SkIntToScalar(-x),
177 SkIntToScalar(-y));
178 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 totalClip.translate(-x, -y, &fClip);
181 }
182
reed@google.com045e62d2011-10-24 12:19:46 +0000183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000186
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkRegion::kDifference_Op);
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#ifdef SK_DEBUG
195 if (!fClip.isEmpty()) {
196 SkIRect deviceR;
197 deviceR.set(0, 0, width, height);
198 SkASSERT(deviceR.contains(fClip.getBounds()));
199 }
200#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000201 }
202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000204 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207/* This is the record we keep for each save/restore level in the stack.
208 Since a level optionally copies the matrix and/or stack, we have pointers
209 for these fields. If the value is copied for this level, the copy is
210 stored in the ...Storage field, and the pointer points to that. If the
211 value is not copied for this level, we ignore ...Storage, and just point
212 at the corresponding value in the previous level in the stack.
213*/
214class SkCanvas::MCRec {
215public:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000216 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
219 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 DeviceCM* fLayer;
222 /* If there are any layers in the stack, this points to the top-most
223 one that is at or below this level in the stack (so we know what
224 bitmap/device to draw into from this level. This value is NOT
225 reference counted, since the real owner is either our fLayer field,
226 or a previous one in a lower level.)
227 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000228 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000230 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if (NULL != prev) {
232 if (flags & SkCanvas::kMatrix_SaveFlag) {
233 fMatrixStorage = *prev->fMatrix;
234 fMatrix = &fMatrixStorage;
235 } else {
236 fMatrix = prev->fMatrix;
237 }
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000240 fRasterClipStorage = *prev->fRasterClip;
241 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 } else {
reed@google.com00177082011-10-12 14:34:30 +0000243 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
245
246 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000247 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 fTopLayer = prev->fTopLayer;
250 } else { // no prev
251 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000254 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = NULL;
256 fTopLayer = NULL;
257 }
258 fLayer = NULL;
259
260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000264 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDELETE(fLayer);
266 dec_rec();
267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
reed@google.com00177082011-10-12 14:34:30 +0000270 SkMatrix fMatrixStorage;
271 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
274class SkDrawIter : public SkDraw {
275public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000277 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000278 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 canvas->updateDeviceCMCache();
280
reed@google.com90c07ea2012-04-13 13:50:27 +0000281 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fBounder = canvas->getBounder();
283 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000284 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com4b226022011-01-11 18:32:13 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 bool next() {
288 // skip over recs with empty clips
289 if (fSkipEmptyClips) {
290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
291 fCurrLayer = fCurrLayer->fNext;
292 }
293 }
294
reed@google.comf68c5e22012-02-24 16:38:58 +0000295 const DeviceCM* rec = fCurrLayer;
296 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297
298 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
300 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 fDevice = rec->fDevice;
302 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000304 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 fCurrLayer = rec->fNext;
307 if (fBounder) {
308 fBounder->setClip(fClip);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313 }
314 return false;
315 }
reed@google.com4b226022011-01-11 18:32:13 +0000316
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000317 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000318 int getX() const { return fDevice->getOrigin().x(); }
319 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 const SkMatrix& getMatrix() const { return *fMatrix; }
321 const SkRegion& getClip() const { return *fClip; }
322 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324private:
325 SkCanvas* fCanvas;
326 const DeviceCM* fCurrLayer;
327 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 SkBool8 fSkipEmptyClips;
329
330 typedef SkDraw INHERITED;
331};
332
333/////////////////////////////////////////////////////////////////////////////
334
335class AutoDrawLooper {
336public:
reed@google.com8926b162012-03-23 15:36:36 +0000337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000338 bool skipLayerForImageFilter = false,
339 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000344 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
reed@google.com8926b162012-03-23 15:36:36 +0000347 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
348 SkPaint tmp;
349 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000350 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
351 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000352 // we'll clear the imageFilter for the actual draws in next(), so
353 // it will only be applied during the restore().
354 fDoClearImageFilter = true;
355 }
356
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000357 if (SkDrawLooper* looper = paint.getLooper()) {
358 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
359 looper->contextSize());
360 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000361 fIsSimple = false;
362 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000363 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000364 // can we be marked as simple?
365 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000366 }
367 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000370 if (fDoClearImageFilter) {
371 fCanvas->internalRestore();
372 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000375
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 const SkPaint& paint() const {
377 SkASSERT(fPaint);
378 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000380
reed@google.com129ec222012-05-15 13:24:09 +0000381 bool next(SkDrawFilter::Type drawType) {
382 if (fDone) {
383 return false;
384 } else if (fIsSimple) {
385 fDone = true;
386 fPaint = &fOrigPaint;
387 return !fPaint->nothingToDraw();
388 } else {
389 return this->doNext(drawType);
390 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000391 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000394 SkLazyPaint fLazyPaint;
395 SkCanvas* fCanvas;
396 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000397 SkDrawFilter* fFilter;
398 const SkPaint* fPaint;
399 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000400 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000401 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000402 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000403 SkDrawLooper::Context* fLooperContext;
404 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000405
406 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407};
408
reed@google.com129ec222012-05-15 13:24:09 +0000409bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000410 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000411 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000412 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000413
414 SkPaint* paint = fLazyPaint.set(fOrigPaint);
415
416 if (fDoClearImageFilter) {
417 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000420 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000422 return false;
423 }
424 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000425 if (!fFilter->filter(paint, drawType)) {
426 fDone = true;
427 return false;
428 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000429 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000430 // no looper means we only draw once
431 fDone = true;
432 }
433 }
434 fPaint = paint;
435
436 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000437 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000438 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000439 }
440
441 // call this after any possible paint modifiers
442 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 fPaint = NULL;
444 return false;
445 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000446 return true;
447}
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449/* Stack helper for managing a SkBounder. In the destructor, if we were
450 given a bounder, we call its commit() method, signifying that we are
451 done accumulating bounds for that draw.
452*/
453class SkAutoBounderCommit {
454public:
455 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
456 ~SkAutoBounderCommit() {
457 if (NULL != fBounder) {
458 fBounder->commit();
459 }
460 }
461private:
462 SkBounder* fBounder;
463};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000464#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465
466#include "SkColorPriv.h"
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468////////// macros to place around the internal draw calls //////////////////
469
reed@google.com8926b162012-03-23 15:36:36 +0000470#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000471 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000472 AutoDrawLooper looper(this, paint, true); \
473 while (looper.next(type)) { \
474 SkAutoBounderCommit ac(fBounder); \
475 SkDrawIter iter(this);
476
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000477#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000478 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000479 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000480 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 SkAutoBounderCommit ac(fBounder); \
482 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000483
reed@google.com4e2b3d32011-04-07 14:18:59 +0000484#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485
486////////////////////////////////////////////////////////////////////////////
487
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000488SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000490 fCachedLocalClipBounds.setEmpty();
491 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000492 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000493 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000494 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000495 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000496 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000497 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
500 new (fMCRec) MCRec(NULL, 0);
501
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000502 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reed@google.com97af1a62012-08-28 12:19:02 +0000505 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000506
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000507 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508}
509
reed@google.comcde92112011-07-06 20:00:52 +0000510SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000511 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
512{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000513 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000514
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000515 this->init(NULL);
516}
517
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000518SkCanvas::SkCanvas(int width, int height)
519 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
520{
521 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000522
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000523 SkBitmap bitmap;
reed@google.com900ecf22014-02-20 20:55:37 +0000524 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
526}
527
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000528SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000529 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
530{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 inc_canvas();
532
533 this->init(device);
534}
535
536SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
538{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 inc_canvas();
540
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000541 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542}
543
544SkCanvas::~SkCanvas() {
545 // free up the contents of our deque
546 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000547 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000548
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 this->internalRestore(); // restore the last, since we're going away
550
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000551 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000552 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000553
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 dec_canvas();
555}
556
557SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
558 SkRefCnt_SafeAssign(fBounder, bounder);
559 return bounder;
560}
561
562SkDrawFilter* SkCanvas::getDrawFilter() const {
563 return fMCRec->fFilter;
564}
565
566SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
567 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
568 return filter;
569}
570
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000571SkMetaData& SkCanvas::getMetaData() {
572 // metadata users are rare, so we lazily allocate it. If that changes we
573 // can decide to just make it a field in the device (rather than a ptr)
574 if (NULL == fMetaData) {
575 fMetaData = new SkMetaData;
576 }
577 return *fMetaData;
578}
579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580///////////////////////////////////////////////////////////////////////////////
581
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000582void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000583 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000584 if (device) {
585 device->flush();
586 }
587}
588
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000589SkISize SkCanvas::getTopLayerSize() const {
590 SkBaseDevice* d = this->getTopDevice();
591 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
592}
593
594SkIPoint SkCanvas::getTopLayerOrigin() const {
595 SkBaseDevice* d = this->getTopDevice();
596 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
597}
598
599SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000600 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000601 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
602}
603
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000604SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000606 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 SkASSERT(rec && rec->fLayer);
608 return rec->fLayer->fDevice;
609}
610
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000611SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000612 if (updateMatrixClip) {
613 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
614 }
reed@google.com9266fed2011-03-30 00:18:03 +0000615 return fMCRec->fTopLayer->fDevice;
616}
617
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000618SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000620 SkDeque::F2BIter iter(fMCStack);
621 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000623 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624
625 if (rootDevice == device) {
626 return device;
627 }
reed@google.com4b226022011-01-11 18:32:13 +0000628
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000630 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 }
632 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000633 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
635
636 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
637 rootDevice = device;
638
639 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 /* Now we update our initial region to have the bounds of the new device,
642 and then intersect all of the clips in our stack with these bounds,
643 to ensure that we can't draw outside of the device's bounds (and trash
644 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 NOTE: this is only a partial-fix, since if the new device is larger than
647 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000648 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
650 reconstruct the correct clips, so this approximation will have to do.
651 The caller really needs to restore() back to the base if they want to
652 accurately take advantage of the new device bounds.
653 */
654
reed@google.com42aea282012-03-28 16:19:15 +0000655 SkIRect bounds;
656 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000658 } else {
659 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 }
reed@google.com42aea282012-03-28 16:19:15 +0000661 // now jam our 1st clip to be bounds, and intersect the rest with that
662 rec->fRasterClip->setRect(bounds);
663 while ((rec = (MCRec*)iter.next()) != NULL) {
664 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
665 }
666
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 return device;
668}
669
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000670bool SkCanvas::readPixels(SkBitmap* bitmap,
671 int x, int y,
672 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000673 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000674 if (!device) {
675 return false;
676 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000677 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000678}
679
bsalomon@google.comc6980972011-11-02 19:57:21 +0000680bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000681 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000682 if (!device) {
683 return false;
684 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000685
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000686 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000687 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000688 if (!bounds.intersect(srcRect)) {
689 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000690 }
691
692 SkBitmap tmp;
693 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
694 bounds.height());
695 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
696 bitmap->swap(tmp);
697 return true;
698 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000699 return false;
700 }
reed@google.com51df9e32010-12-23 19:29:18 +0000701}
702
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000703#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000704void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
705 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000706 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000707 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000708 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
709 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
710 device->accessBitmap(true);
711 device->writePixels(bitmap, x, y, config8888);
712 }
reed@google.com51df9e32010-12-23 19:29:18 +0000713 }
714}
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000715#endif
716
717bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
718 if (bitmap.getTexture()) {
719 return false;
720 }
721 SkBitmap bm(bitmap);
722 bm.lockPixels();
723 if (bm.getPixels()) {
724 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
725 }
726 return false;
727}
728
729bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
730 int x, int y) {
731 switch (origInfo.colorType()) {
732 case kUnknown_SkColorType:
733 case kIndex_8_SkColorType:
734 return false;
735 default:
736 break;
737 }
738 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
739 return false;
740 }
741
742 const SkISize size = this->getBaseLayerSize();
743 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
744 if (!target.intersect(0, 0, size.width(), size.height())) {
745 return false;
746 }
747
748 SkBaseDevice* device = this->getDevice();
749 if (!device) {
750 return false;
751 }
752
753 SkImageInfo info = origInfo;
754 // the intersect may have shrunk info's logical size
755 info.fWidth = target.width();
756 info.fHeight = target.height();
757
758 // if x or y are negative, then we have to adjust pixels
759 if (x > 0) {
760 x = 0;
761 }
762 if (y > 0) {
763 y = 0;
764 }
765 // here x,y are either 0 or negative
766 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
767
768 // The device can assert that the requested area is always contained in its bounds
769 return device->writePixelsDirect(info, pixels, rowBytes, target.x(), target.y());
770}
reed@google.com51df9e32010-12-23 19:29:18 +0000771
junov@google.com4370aed2012-01-18 16:21:08 +0000772SkCanvas* SkCanvas::canvasForDrawIter() {
773 return this;
774}
775
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776//////////////////////////////////////////////////////////////////////////////
777
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778void SkCanvas::updateDeviceCMCache() {
779 if (fDeviceCMDirty) {
780 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000781 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000785 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000787 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000789 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 } while ((layer = layer->fNext) != NULL);
791 }
792 fDeviceCMDirty = false;
793 }
794}
795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796///////////////////////////////////////////////////////////////////////////////
797
798int SkCanvas::internalSave(SaveFlags flags) {
799 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000800
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 MCRec* newTop = (MCRec*)fMCStack.push_back();
802 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000803
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000805
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000806 if (SkCanvas::kClip_SaveFlag & flags) {
807 fClipStack.save();
808 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 return saveCount;
811}
812
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000813void SkCanvas::willSave(SaveFlags) {
814 // Do nothing. Subclasses may do something.
815}
816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000818 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 // call shared impl
820 return this->internalSave(flags);
821}
822
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000824#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000826#else
827 return true;
828#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829}
830
junov@chromium.orga907ac32012-02-24 21:54:07 +0000831bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000832 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000833 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000834 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000835 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000836 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000837 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000838
839 if (imageFilter) {
840 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
841 // Filters may grow the bounds beyond the device bounds.
842 op = SkRegion::kReplace_Op;
843 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000844 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 if (NULL != bounds) {
846 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000847
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 this->getTotalMatrix().mapRect(&r, *bounds);
849 r.roundOut(&ir);
850 // early exit if the layer's bounds are clipped out
851 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000852 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000853 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000854 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 }
857 } else { // no user bounds, so just use the clip
858 ir = clipBounds;
859 }
860
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000861 if (bounds_affects_clip(flags)) {
862 fClipStack.clipDevRect(ir, op);
863 // early exit if the clip is now empty
864 if (!fMCRec->fRasterClip->op(ir, op)) {
865 return false;
866 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000867 }
868
869 if (intersection) {
870 *intersection = ir;
871 }
872 return true;
873}
874
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000875SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) {
876
877 // Do nothing. Subclasses may do something.
878 return kFullLayer_SaveLayerStrategy;
879}
880
junov@chromium.orga907ac32012-02-24 21:54:07 +0000881int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
882 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000883 // Overriding classes may return false to signal that we don't need to create a layer.
884 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
885 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000886}
887
reed@google.com76f10a32014-02-05 15:32:21 +0000888static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000889 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000890 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000891 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000892}
893
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000894int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
895 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000896#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
897 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
898#endif
899
junov@chromium.orga907ac32012-02-24 21:54:07 +0000900 // do this before we create the layer. We don't call the public save() since
901 // that would invoke a possibly overridden virtual
902 int count = this->internalSave(flags);
903
904 fDeviceCMDirty = true;
905
906 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000907 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 return count;
909 }
910
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000911 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
912 // the clipRectBounds() call above?
913 if (kNoLayer_SaveLayerStrategy == strategy) {
914 return count;
915 }
916
reed@google.comb55deeb2012-01-06 14:43:09 +0000917 // Kill the imagefilter if our device doesn't allow it
918 SkLazyPaint lazyP;
919 if (paint && paint->getImageFilter()) {
920 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000921 if (justForImageFilter) {
922 // early exit if the layer was just for the imageFilter
923 return count;
924 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000925 SkPaint* p = lazyP.set(*paint);
926 p->setImageFilter(NULL);
927 paint = p;
928 }
929 }
930
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000931 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
932 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
933 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000935 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000936 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000937 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000938 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000939 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000940 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000941 if (NULL == device) {
942 SkDebugf("Unable to create device for layer.");
943 return count;
944 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000945
reed@google.com6f8f2922011-03-04 22:27:10 +0000946 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000947 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 device->unref();
949
950 layer->fNext = fMCRec->fTopLayer;
951 fMCRec->fLayer = layer;
952 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
953
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000954 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 return count;
956}
957
958int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
959 SaveFlags flags) {
960 if (0xFF == alpha) {
961 return this->saveLayer(bounds, NULL, flags);
962 } else {
963 SkPaint tmpPaint;
964 tmpPaint.setAlpha(alpha);
965 return this->saveLayer(bounds, &tmpPaint, flags);
966 }
967}
968
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000969void SkCanvas::willRestore() {
970 // Do nothing. Subclasses may do something.
971}
972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973void SkCanvas::restore() {
974 // check for underflow
975 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000976 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 this->internalRestore();
978 }
979}
980
981void SkCanvas::internalRestore() {
982 SkASSERT(fMCStack.count() != 0);
983
984 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000985 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000987 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
988 fClipStack.restore();
989 }
990
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000991 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 DeviceCM* layer = fMCRec->fLayer; // may be null
993 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
994 fMCRec->fLayer = NULL;
995
996 // now do the normal restore()
997 fMCRec->~MCRec(); // balanced in save()
998 fMCStack.pop_back();
999 fMCRec = (MCRec*)fMCStack.back();
1000
1001 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1002 since if we're being recorded, we don't want to record this (the
1003 recorder will have already recorded the restore).
1004 */
1005 if (NULL != layer) {
1006 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001007 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001008 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1009 layer->fPaint);
1010 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001012
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001013 SkASSERT(fSaveLayerCount > 0);
1014 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 }
1016 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001017 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018}
1019
1020int SkCanvas::getSaveCount() const {
1021 return fMCStack.count();
1022}
1023
1024void SkCanvas::restoreToCount(int count) {
1025 // sanity check
1026 if (count < 1) {
1027 count = 1;
1028 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001029
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001030 int n = this->getSaveCount() - count;
1031 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 this->restore();
1033 }
1034}
1035
reed@google.com7c202932011-12-14 18:48:05 +00001036bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001037 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001038}
1039
reed@google.com76f10a32014-02-05 15:32:21 +00001040SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1041 return this->onNewSurface(info);
1042}
1043
1044SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1045 SkBaseDevice* dev = this->getDevice();
1046 return dev ? dev->newSurface(info) : NULL;
1047}
1048
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001049SkImageInfo SkCanvas::imageInfo() const {
1050 SkBaseDevice* dev = this->getDevice();
1051 if (dev) {
1052 return dev->imageInfo();
1053 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001054 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001055 }
1056}
1057
1058const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1059 return this->onPeekPixels(info, rowBytes);
1060}
1061
1062const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1063 SkBaseDevice* dev = this->getDevice();
1064 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1065}
1066
reed@google.com9c135db2014-03-12 18:28:35 +00001067void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1068 return this->onAccessTopLayerPixels(info, rowBytes);
1069}
1070
1071void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1072 SkBaseDevice* dev = this->getTopDevice();
1073 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1074}
1075
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001076SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1077 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1078 if (NULL == fAddr) {
1079 fInfo = canvas->imageInfo();
1080 if (kUnknown_SkColorType == fInfo.colorType() ||
1081 !fBitmap.allocPixels(fInfo))
1082 {
1083 return; // failure, fAddr is NULL
1084 }
1085 fBitmap.lockPixels();
1086 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1087 return; // failure, fAddr is NULL
1088 }
1089 fAddr = fBitmap.getPixels();
1090 fRowBytes = fBitmap.rowBytes();
1091 }
1092 SkASSERT(fAddr); // success
1093}
1094
1095bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1096 if (fAddr) {
1097 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1098 NULL, NULL);
1099 } else {
1100 bitmap->reset();
1101 return false;
1102 }
1103}
1104
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001105void SkCanvas::onPushCull(const SkRect& cullRect) {
1106 // do nothing. Subclasses may do something
1107}
1108
1109void SkCanvas::onPopCull() {
1110 // do nothing. Subclasses may do something
1111}
1112
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113/////////////////////////////////////////////////////////////////////////////
1114
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001115void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001117 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 return;
1119 }
1120
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001121 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001123 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001125
1126 SkDEBUGCODE(bitmap.validate();)
1127 CHECK_LOCKCOUNT_BALANCE(bitmap);
1128
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001129 SkRect storage;
1130 const SkRect* bounds = NULL;
1131 if (paint && paint->canComputeFastBounds()) {
1132 bitmap.getBounds(&storage);
1133 matrix.mapRect(&storage);
1134 bounds = &paint->computeFastBounds(storage, &storage);
1135 }
1136
1137 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001138
1139 while (iter.next()) {
1140 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1141 }
1142
1143 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144}
1145
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001146void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001147 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 SkPaint tmp;
1149 if (NULL == paint) {
1150 tmp.setDither(true);
1151 paint = &tmp;
1152 }
reed@google.com4b226022011-01-11 18:32:13 +00001153
reed@google.com8926b162012-03-23 15:36:36 +00001154 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001156 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001157 paint = &looper.paint();
1158 SkImageFilter* filter = paint->getImageFilter();
1159 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001160 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001161 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001162 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001163 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001164 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001165 SkMatrix matrix = *iter.fMatrix;
1166 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001167 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
1168 SkImageFilter::Context ctx(matrix, clipBounds);
1169 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001170 SkPaint tmpUnfiltered(*paint);
1171 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001172 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1173 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001174 }
1175 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001176 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001177 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001179 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180}
1181
reed@google.com8926b162012-03-23 15:36:36 +00001182void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1183 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001184 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001185 return;
1186 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001187 SkDEBUGCODE(bitmap.validate();)
1188 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001189
reed@google.com8926b162012-03-23 15:36:36 +00001190 SkPaint tmp;
1191 if (NULL == paint) {
1192 paint = &tmp;
1193 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001194
reed@google.com8926b162012-03-23 15:36:36 +00001195 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001196
reed@google.com8926b162012-03-23 15:36:36 +00001197 while (iter.next()) {
1198 paint = &looper.paint();
1199 SkImageFilter* filter = paint->getImageFilter();
1200 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1201 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001202 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001203 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001204 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001205 SkMatrix matrix = *iter.fMatrix;
1206 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001207 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1208 SkImageFilter::Context ctx(matrix, clipBounds);
1209 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001210 SkPaint tmpUnfiltered(*paint);
1211 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001212 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001213 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001214 }
1215 } else {
1216 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1217 }
1218 }
1219 LOOPER_END
1220}
1221
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001223void SkCanvas::didTranslate(SkScalar, SkScalar) {
1224 // Do nothing. Subclasses may do something.
1225}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226
1227bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1228 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001229 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001230 bool res = fMCRec->fMatrix->preTranslate(dx, dy);
1231
1232 this->didTranslate(dx, dy);
1233 return res;
1234}
1235
1236void SkCanvas::didScale(SkScalar, SkScalar) {
1237 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238}
1239
1240bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1241 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001242 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001243 bool res = fMCRec->fMatrix->preScale(sx, sy);
1244
1245 this->didScale(sx, sy);
1246 return res;
1247}
1248
1249void SkCanvas::didRotate(SkScalar) {
1250 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251}
1252
1253bool SkCanvas::rotate(SkScalar degrees) {
1254 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001255 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001256 bool res = fMCRec->fMatrix->preRotate(degrees);
1257
1258 this->didRotate(degrees);
1259 return res;
1260}
1261
1262void SkCanvas::didSkew(SkScalar, SkScalar) {
1263 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264}
1265
1266bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1267 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001268 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001269 bool res = fMCRec->fMatrix->preSkew(sx, sy);
1270
1271 this->didSkew(sx, sy);
1272 return res;
1273}
1274
1275void SkCanvas::didConcat(const SkMatrix&) {
1276 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277}
1278
1279bool SkCanvas::concat(const SkMatrix& matrix) {
1280 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001281 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001282 bool res = fMCRec->fMatrix->preConcat(matrix);
1283
1284 this->didConcat(matrix);
1285 return res;
1286}
1287
1288void SkCanvas::didSetMatrix(const SkMatrix&) {
1289 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290}
1291
1292void SkCanvas::setMatrix(const SkMatrix& matrix) {
1293 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001294 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001296 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297}
1298
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299void SkCanvas::resetMatrix() {
1300 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001301
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 matrix.reset();
1303 this->setMatrix(matrix);
1304}
1305
1306//////////////////////////////////////////////////////////////////////////////
1307
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001308void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001309 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1310 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001311}
1312
1313void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001314#ifdef SK_ENABLE_CLIP_QUICKREJECT
1315 if (SkRegion::kIntersect_Op == op) {
1316 if (fMCRec->fRasterClip->isEmpty()) {
1317 return false;
1318 }
1319
reed@google.com3b3e8952012-08-16 20:53:31 +00001320 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001321 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001322 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001323
1324 fClipStack.clipEmpty();
1325 return fMCRec->fRasterClip->setEmpty();
1326 }
1327 }
1328#endif
1329
reed@google.com5c3d1472011-02-22 19:12:23 +00001330 AutoValidateClip avc(this);
1331
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001333 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001334 if (!fAllowSoftClip) {
1335 edgeStyle = kHard_ClipEdgeStyle;
1336 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337
1338 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001339 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001340 // the matrix. This means we don't have to a) make a path, and b) tell
1341 // the region code to scan-convert the path, only to discover that it
1342 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344
1345 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001346 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1347 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001349 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001350 // and clip against that, since it can handle any matrix. However, to
1351 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1352 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 SkPath path;
1354
1355 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001356 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 }
1358}
1359
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001360static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1361 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001362 // base is used to limit the size (and therefore memory allocation) of the
1363 // region that results from scan converting devPath.
1364 SkRegion base;
1365
reed@google.com819c9212011-02-23 18:56:55 +00001366 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001367 // since we are intersect, we can do better (tighter) with currRgn's
1368 // bounds, than just using the device. However, if currRgn is complex,
1369 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001370 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001371 // FIXME: we should also be able to do this when currClip->isBW(),
1372 // but relaxing the test above triggers GM asserts in
1373 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001374 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001375 } else {
reed@google.com00177082011-10-12 14:34:30 +00001376 base.setRect(currClip->getBounds());
1377 SkRasterClip clip;
1378 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001379 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001380 }
reed@google.com819c9212011-02-23 18:56:55 +00001381 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001382 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001383 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001384 currClip->setEmpty();
1385 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001386 }
1387
junov@chromium.orga907ac32012-02-24 21:54:07 +00001388 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001389
1390 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001391 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001392 } else {
reed@google.com00177082011-10-12 14:34:30 +00001393 SkRasterClip clip;
1394 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001395 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001396 }
1397 }
1398}
1399
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001400void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001401 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001402 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001403 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1404 } else {
1405 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001406 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001407}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001408
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001409void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001410 SkRRect transformedRRect;
1411 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1412 AutoValidateClip avc(this);
1413
1414 fDeviceCMDirty = true;
1415 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001416 if (!fAllowSoftClip) {
1417 edgeStyle = kHard_ClipEdgeStyle;
1418 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001419
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001420 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001421
1422 SkPath devPath;
1423 devPath.addRRect(transformedRRect);
1424
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001425 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1426 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001427 }
1428
1429 SkPath path;
1430 path.addRRect(rrect);
1431 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001432 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001433}
1434
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001435void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001436 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1437 SkRect r;
1438 if (!path.isInverseFillType() && path.isRect(&r)) {
1439 this->onClipRect(r, op, edgeStyle);
1440 } else {
1441 this->onClipPath(path, op, edgeStyle);
1442 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001443}
1444
1445void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001446#ifdef SK_ENABLE_CLIP_QUICKREJECT
1447 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1448 if (fMCRec->fRasterClip->isEmpty()) {
1449 return false;
1450 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001451
reed@google.com3b3e8952012-08-16 20:53:31 +00001452 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001453 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001454 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001455
reed@google.comda17f752012-08-16 18:27:05 +00001456 fClipStack.clipEmpty();
1457 return fMCRec->fRasterClip->setEmpty();
1458 }
1459 }
1460#endif
1461
reed@google.com5c3d1472011-02-22 19:12:23 +00001462 AutoValidateClip avc(this);
1463
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001465 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001466 if (!fAllowSoftClip) {
1467 edgeStyle = kHard_ClipEdgeStyle;
1468 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469
1470 SkPath devPath;
1471 path.transform(*fMCRec->fMatrix, &devPath);
1472
reed@google.comfe701122011-11-08 19:41:23 +00001473 // Check if the transfomation, or the original path itself
1474 // made us empty. Note this can also happen if we contained NaN
1475 // values. computing the bounds detects this, and will set our
1476 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1477 if (devPath.getBounds().isEmpty()) {
1478 // resetting the path will remove any NaN or other wanky values
1479 // that might upset our scan converter.
1480 devPath.reset();
1481 }
1482
reed@google.com5c3d1472011-02-22 19:12:23 +00001483 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001484 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001485
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001486 if (fAllowSimplifyClip) {
1487 devPath.reset();
1488 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1489 const SkClipStack* clipStack = getClipStack();
1490 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1491 const SkClipStack::Element* element;
1492 while ((element = iter.next())) {
1493 SkClipStack::Element::Type type = element->getType();
1494 if (type == SkClipStack::Element::kEmpty_Type) {
1495 continue;
1496 }
1497 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001498 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001499 SkRegion::Op elementOp = element->getOp();
1500 if (elementOp == SkRegion::kReplace_Op) {
1501 devPath = operand;
1502 } else {
1503 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1504 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001505 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1506 // perhaps we need an API change to avoid this sort of mixed-signals about
1507 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 if (element->isAA()) {
1509 edgeStyle = kSoft_ClipEdgeStyle;
1510 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001511 }
1512 op = SkRegion::kReplace_Op;
1513 }
1514
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001515 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516}
1517
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001518void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001519 bool inverseFilled) {
1520 // This is for updating the clip conservatively using only bounds
1521 // information.
1522 // Contract:
1523 // The current clip must contain the true clip. The true
1524 // clip is the clip that would have normally been computed
1525 // by calls to clipPath and clipRRect
1526 // Objective:
1527 // Keep the current clip as small as possible without
1528 // breaking the contract, using only clip bounding rectangles
1529 // (for performance).
1530
1531 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1532 // don't have to worry about getting caught in a loop. Thus anywhere
1533 // we call a virtual method, we explicitly prefix it with
1534 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001535
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001536 if (inverseFilled) {
1537 switch (op) {
1538 case SkRegion::kIntersect_Op:
1539 case SkRegion::kDifference_Op:
1540 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001541 // the clip unchanged conservatively respects the contract.
1542 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001543 case SkRegion::kUnion_Op:
1544 case SkRegion::kReplace_Op:
1545 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001546 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001547 // These ops can grow the current clip up to the extents of
1548 // the input clip, which is inverse filled, so we just set
1549 // the current clip to the device bounds.
1550 SkRect deviceBounds;
1551 SkIRect deviceIBounds;
1552 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001553 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001554 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1555 // set the clip in device space
1556 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001557 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001558 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001559 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001560 break;
1561 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001562 default:
1563 SkASSERT(0); // unhandled op?
1564 }
1565 } else {
1566 // Not inverse filled
1567 switch (op) {
1568 case SkRegion::kIntersect_Op:
1569 case SkRegion::kUnion_Op:
1570 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001571 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1572 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001573 case SkRegion::kDifference_Op:
1574 // Difference can only shrink the current clip.
1575 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001576 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001577 case SkRegion::kReverseDifference_Op:
1578 // To reverse, we swap in the bounds with a replace op.
1579 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001580 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1581 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001582 case SkRegion::kXOR_Op:
1583 // Be conservative, based on (A XOR B) always included in (A union B),
1584 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1586 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001587 default:
1588 SkASSERT(0); // unhandled op?
1589 }
1590 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001591}
1592
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001593void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595}
1596
1597void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001598 AutoValidateClip avc(this);
1599
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001601 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602
reed@google.com5c3d1472011-02-22 19:12:23 +00001603 // todo: signal fClipStack that we have a region, and therefore (I guess)
1604 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001605 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001606
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001607 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
reed@google.com819c9212011-02-23 18:56:55 +00001610#ifdef SK_DEBUG
1611void SkCanvas::validateClip() const {
1612 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001613 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001614 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001615 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001616 return;
1617 }
1618
reed@google.com819c9212011-02-23 18:56:55 +00001619 SkIRect ir;
1620 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001621 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001622
robertphillips@google.com80214e22012-07-20 15:33:18 +00001623 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001624 const SkClipStack::Element* element;
1625 while ((element = iter.next()) != NULL) {
1626 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001627 case SkClipStack::Element::kRect_Type:
1628 element->getRect().round(&ir);
1629 tmpClip.op(ir, element->getOp());
1630 break;
1631 case SkClipStack::Element::kEmpty_Type:
1632 tmpClip.setEmpty();
1633 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001634 default: {
1635 SkPath path;
1636 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001638 break;
1639 }
reed@google.com819c9212011-02-23 18:56:55 +00001640 }
1641 }
reed@google.com819c9212011-02-23 18:56:55 +00001642}
1643#endif
1644
reed@google.com90c07ea2012-04-13 13:50:27 +00001645void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001646 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001647 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001648
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001649 static const SkRect kEmpty = { 0, 0, 0, 0 };
1650 while ((element = iter.next()) != NULL) {
1651 switch (element->getType()) {
1652 case SkClipStack::Element::kPath_Type:
1653 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1654 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001655 case SkClipStack::Element::kRRect_Type:
1656 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1657 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001658 case SkClipStack::Element::kRect_Type:
1659 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1660 break;
1661 case SkClipStack::Element::kEmpty_Type:
1662 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1663 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001664 }
1665 }
1666}
1667
reed@google.com5c3d1472011-02-22 19:12:23 +00001668///////////////////////////////////////////////////////////////////////////////
1669
reed@google.com754de5f2014-02-24 19:38:20 +00001670bool SkCanvas::isClipEmpty() const {
1671 return fMCRec->fRasterClip->isEmpty();
1672}
1673
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001674bool SkCanvas::isClipRect() const {
1675 return fMCRec->fRasterClip->isRect();
1676}
1677
reed@google.com3b3e8952012-08-16 20:53:31 +00001678bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001679
reed@google.com16078632011-12-06 18:56:37 +00001680 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001681 return true;
1682
reed@google.com00177082011-10-12 14:34:30 +00001683 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684 return true;
1685 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686
tomhudson@google.com8d430182011-06-06 19:11:19 +00001687 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001688 SkRect dst;
1689 fMCRec->fMatrix->mapRect(&dst, rect);
1690 SkIRect idst;
1691 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001692 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001693 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001694 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001695
reed@android.coma380ae42009-07-21 01:17:02 +00001696 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001697 // TODO: should we use | instead, or compare all 4 at once?
1698 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001699 return true;
1700 }
reed@google.comc0784db2013-12-13 21:16:12 +00001701 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001702 return true;
1703 }
1704 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706}
1707
reed@google.com3b3e8952012-08-16 20:53:31 +00001708bool SkCanvas::quickReject(const SkPath& path) const {
1709 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710}
1711
reed@google.com3b3e8952012-08-16 20:53:31 +00001712bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001713 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001714 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715 return false;
1716 }
1717
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001718 SkMatrix inverse;
1719 // if we can't invert the CTM, we can't return local clip bounds
1720 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001721 if (bounds) {
1722 bounds->setEmpty();
1723 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001724 return false;
1725 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001727 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001728 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001729 // adjust it outwards in case we are antialiasing
1730 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001731
reed@google.com8f4d2302013-12-17 16:44:46 +00001732 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1733 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734 inverse.mapRect(bounds, r);
1735 }
1736 return true;
1737}
1738
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001739bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001740 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001741 if (clip.isEmpty()) {
1742 if (bounds) {
1743 bounds->setEmpty();
1744 }
1745 return false;
1746 }
1747
1748 if (NULL != bounds) {
1749 *bounds = clip.getBounds();
1750 }
1751 return true;
1752}
1753
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754const SkMatrix& SkCanvas::getTotalMatrix() const {
1755 return *fMCRec->fMatrix;
1756}
1757
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001758#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001759SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001760 if (fMCRec->fRasterClip->isEmpty()) {
1761 return kEmpty_ClipType;
1762 }
1763 if (fMCRec->fRasterClip->isRect()) {
1764 return kRect_ClipType;
1765 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001766 return kComplex_ClipType;
1767}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001768#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001769
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001770#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001772 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001774#endif
1775
1776const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1777 return fMCRec->fRasterClip->forceGetBW();
1778}
1779
1780void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1781 path->reset();
1782
1783 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1784 if (rgn.isEmpty()) {
1785 return;
1786 }
1787 (void)rgn.getBoundaryPath(path);
1788}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789
reed@google.com9c135db2014-03-12 18:28:35 +00001790GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1791 SkBaseDevice* dev = this->getTopDevice();
1792 return dev ? dev->accessRenderTarget() : NULL;
1793}
1794
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001795SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001796 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001797 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798}
1799
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001800GrContext* SkCanvas::getGrContext() {
1801#if SK_SUPPORT_GPU
1802 SkBaseDevice* device = this->getTopDevice();
1803 if (NULL != device) {
1804 GrRenderTarget* renderTarget = device->accessRenderTarget();
1805 if (NULL != renderTarget) {
1806 return renderTarget->getContext();
1807 }
1808 }
1809#endif
1810
1811 return NULL;
1812
1813}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001814
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001815void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1816 const SkPaint& paint) {
1817 if (outer.isEmpty()) {
1818 return;
1819 }
1820 if (inner.isEmpty()) {
1821 this->drawRRect(outer, paint);
1822 return;
1823 }
1824
1825 // We don't have this method (yet), but technically this is what we should
1826 // be able to assert...
1827 // SkASSERT(outer.contains(inner));
1828 //
1829 // For now at least check for containment of bounds
1830 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1831
1832 this->onDrawDRRect(outer, inner, paint);
1833}
1834
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835//////////////////////////////////////////////////////////////////////////////
1836// These are the virtual drawing methods
1837//////////////////////////////////////////////////////////////////////////////
1838
reed@google.com2a981812011-04-14 18:59:28 +00001839void SkCanvas::clear(SkColor color) {
1840 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001841 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001842 while (iter.next()) {
1843 iter.fDevice->clear(color);
1844 }
1845}
1846
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001848 this->internalDrawPaint(paint);
1849}
1850
1851void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001852 CHECK_SHADER_NOSETCONTEXT(paint);
1853
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001854 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855
1856 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001857 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858 }
1859
reed@google.com4e2b3d32011-04-07 14:18:59 +00001860 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861}
1862
1863void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1864 const SkPaint& paint) {
1865 if ((long)count <= 0) {
1866 return;
1867 }
1868
reed@google.comea033602012-12-14 13:13:55 +00001869 CHECK_SHADER_NOSETCONTEXT(paint);
1870
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001871 SkRect r, storage;
1872 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001873 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001874 // special-case 2 points (common for drawing a single line)
1875 if (2 == count) {
1876 r.set(pts[0], pts[1]);
1877 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001878 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001879 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001880 bounds = &paint.computeFastStrokeBounds(r, &storage);
1881 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001882 return;
1883 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001884 }
reed@google.coma584aed2012-05-16 14:06:02 +00001885
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886 SkASSERT(pts != NULL);
1887
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001888 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001889
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001891 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892 }
reed@google.com4b226022011-01-11 18:32:13 +00001893
reed@google.com4e2b3d32011-04-07 14:18:59 +00001894 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895}
1896
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001897void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001898 CHECK_SHADER_NOSETCONTEXT(paint);
1899
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001900 SkRect storage;
1901 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001902 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001903 bounds = &paint.computeFastBounds(r, &storage);
1904 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905 return;
1906 }
1907 }
reed@google.com4b226022011-01-11 18:32:13 +00001908
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001909 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910
1911 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001912 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 }
1914
reed@google.com4e2b3d32011-04-07 14:18:59 +00001915 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916}
1917
reed@google.com4ed0fb72012-12-12 20:48:18 +00001918void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001919 CHECK_SHADER_NOSETCONTEXT(paint);
1920
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001921 SkRect storage;
1922 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001923 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001924 bounds = &paint.computeFastBounds(oval, &storage);
1925 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001926 return;
1927 }
1928 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001929
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001930 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001931
1932 while (iter.next()) {
1933 iter.fDevice->drawOval(iter, oval, looper.paint());
1934 }
1935
1936 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001937}
1938
1939void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001940 CHECK_SHADER_NOSETCONTEXT(paint);
1941
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001942 SkRect storage;
1943 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001944 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001945 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1946 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001947 return;
1948 }
1949 }
1950
1951 if (rrect.isRect()) {
1952 // call the non-virtual version
1953 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001954 return;
1955 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001956 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001957 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1958 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001959 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001960
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001961 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001962
1963 while (iter.next()) {
1964 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1965 }
1966
1967 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001968}
1969
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001970void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1971 const SkPaint& paint) {
1972 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001973
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001974 SkRect storage;
1975 const SkRect* bounds = NULL;
1976 if (paint.canComputeFastBounds()) {
1977 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1978 if (this->quickReject(*bounds)) {
1979 return;
1980 }
1981 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001982
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001983 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001984
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001985 while (iter.next()) {
1986 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1987 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001988
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001989 LOOPER_END
1990}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001991
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001992void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001993 CHECK_SHADER_NOSETCONTEXT(paint);
1994
reed@google.com93645112012-07-26 16:11:47 +00001995 if (!path.isFinite()) {
1996 return;
1997 }
1998
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001999 SkRect storage;
2000 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002001 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002002 const SkRect& pathBounds = path.getBounds();
2003 bounds = &paint.computeFastBounds(pathBounds, &storage);
2004 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005 return;
2006 }
2007 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002008
2009 const SkRect& r = path.getBounds();
2010 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002011 if (path.isInverseFillType()) {
2012 this->internalDrawPaint(paint);
2013 }
2014 return;
2015 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002017 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002018
2019 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002020 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002021 }
2022
reed@google.com4e2b3d32011-04-07 14:18:59 +00002023 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024}
2025
2026void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2027 const SkPaint* paint) {
2028 SkDEBUGCODE(bitmap.validate();)
2029
reed@google.com3d608122011-11-21 15:16:16 +00002030 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002031 SkRect bounds = {
2032 x, y,
2033 x + SkIntToScalar(bitmap.width()),
2034 y + SkIntToScalar(bitmap.height())
2035 };
2036 if (paint) {
2037 (void)paint->computeFastBounds(bounds, &bounds);
2038 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002039 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 return;
2041 }
2042 }
reed@google.com4b226022011-01-11 18:32:13 +00002043
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 SkMatrix matrix;
2045 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002046 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047}
2048
reed@google.com9987ec32011-09-07 11:57:52 +00002049// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002050void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002051 const SkRect& dst, const SkPaint* paint,
2052 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002053 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054 return;
2055 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002056
reed@google.comea033602012-12-14 13:13:55 +00002057 CHECK_LOCKCOUNT_BALANCE(bitmap);
2058
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002059 SkRect storage;
2060 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002061 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002062 if (paint) {
2063 bounds = &paint->computeFastBounds(dst, &storage);
2064 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002065 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002066 return;
2067 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068 }
reed@google.com3d608122011-11-21 15:16:16 +00002069
reed@google.com33535f32012-09-25 15:37:50 +00002070 SkLazyPaint lazy;
2071 if (NULL == paint) {
2072 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002074
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002075 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002076
reed@google.com33535f32012-09-25 15:37:50 +00002077 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002078 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002079 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002080
reed@google.com33535f32012-09-25 15:37:50 +00002081 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082}
2083
reed@google.com71121732012-09-18 15:14:33 +00002084void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002085 const SkRect& dst, const SkPaint* paint,
2086 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002087 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002088 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002089}
2090
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2092 const SkPaint* paint) {
2093 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002094 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095}
2096
reed@google.com9987ec32011-09-07 11:57:52 +00002097void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2098 const SkIRect& center, const SkRect& dst,
2099 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002100 if (bitmap.drawsNothing()) {
2101 return;
2102 }
reed@google.com3d608122011-11-21 15:16:16 +00002103 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002104 SkRect storage;
2105 const SkRect* bounds = &dst;
2106 if (paint) {
2107 bounds = &paint->computeFastBounds(dst, &storage);
2108 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002109 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002110 return;
2111 }
2112 }
2113
reed@google.com9987ec32011-09-07 11:57:52 +00002114 const int32_t w = bitmap.width();
2115 const int32_t h = bitmap.height();
2116
2117 SkIRect c = center;
2118 // pin center to the bounds of the bitmap
2119 c.fLeft = SkMax32(0, center.fLeft);
2120 c.fTop = SkMax32(0, center.fTop);
2121 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2122 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2123
reed@google.com71121732012-09-18 15:14:33 +00002124 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002125 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002126 };
2127 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002128 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002129 };
reed@google.com9987ec32011-09-07 11:57:52 +00002130 SkScalar dstX[4] = {
2131 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2132 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2133 };
2134 SkScalar dstY[4] = {
2135 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2136 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2137 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002138
reed@google.com9987ec32011-09-07 11:57:52 +00002139 if (dstX[1] > dstX[2]) {
2140 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2141 dstX[2] = dstX[1];
2142 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002143
reed@google.com9987ec32011-09-07 11:57:52 +00002144 if (dstY[1] > dstY[2]) {
2145 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2146 dstY[2] = dstY[1];
2147 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002148
reed@google.com9987ec32011-09-07 11:57:52 +00002149 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002150 SkRect s, d;
2151
reed@google.com9987ec32011-09-07 11:57:52 +00002152 s.fTop = srcY[y];
2153 s.fBottom = srcY[y+1];
2154 d.fTop = dstY[y];
2155 d.fBottom = dstY[y+1];
2156 for (int x = 0; x < 3; x++) {
2157 s.fLeft = srcX[x];
2158 s.fRight = srcX[x+1];
2159 d.fLeft = dstX[x];
2160 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002161 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002162 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002163 }
2164 }
2165}
2166
2167void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2168 const SkRect& dst, const SkPaint* paint) {
2169 SkDEBUGCODE(bitmap.validate();)
2170
2171 // Need a device entry-point, so gpu can use a mesh
2172 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2173}
2174
reed@google.comf67e4cf2011-03-15 20:56:58 +00002175class SkDeviceFilteredPaint {
2176public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002177 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2178 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002179 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002180 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002181 newPaint->setFlags(flags.fFlags);
2182 newPaint->setHinting(flags.fHinting);
2183 fPaint = newPaint;
2184 } else {
2185 fPaint = &paint;
2186 }
2187 }
2188
reed@google.comf67e4cf2011-03-15 20:56:58 +00002189 const SkPaint& paint() const { return *fPaint; }
2190
2191private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002192 const SkPaint* fPaint;
2193 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002194};
2195
bungeman@google.com52c748b2011-08-22 21:30:43 +00002196void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2197 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002198 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002199 draw.fDevice->drawRect(draw, r, paint);
2200 } else {
2201 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002202 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002203 draw.fDevice->drawRect(draw, r, p);
2204 }
2205}
2206
2207void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2208 const char text[], size_t byteLength,
2209 SkScalar x, SkScalar y) {
2210 SkASSERT(byteLength == 0 || text != NULL);
2211
2212 // nothing to draw
2213 if (text == NULL || byteLength == 0 ||
2214 draw.fClip->isEmpty() ||
2215 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2216 return;
2217 }
2218
2219 SkScalar width = 0;
2220 SkPoint start;
2221
2222 start.set(0, 0); // to avoid warning
2223 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2224 SkPaint::kStrikeThruText_Flag)) {
2225 width = paint.measureText(text, byteLength);
2226
2227 SkScalar offsetX = 0;
2228 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2229 offsetX = SkScalarHalf(width);
2230 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2231 offsetX = width;
2232 }
2233 start.set(x - offsetX, y);
2234 }
2235
2236 if (0 == width) {
2237 return;
2238 }
2239
2240 uint32_t flags = paint.getFlags();
2241
2242 if (flags & (SkPaint::kUnderlineText_Flag |
2243 SkPaint::kStrikeThruText_Flag)) {
2244 SkScalar textSize = paint.getTextSize();
2245 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2246 SkRect r;
2247
2248 r.fLeft = start.fX;
2249 r.fRight = start.fX + width;
2250
2251 if (flags & SkPaint::kUnderlineText_Flag) {
2252 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2253 start.fY);
2254 r.fTop = offset;
2255 r.fBottom = offset + height;
2256 DrawRect(draw, paint, r, textSize);
2257 }
2258 if (flags & SkPaint::kStrikeThruText_Flag) {
2259 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2260 start.fY);
2261 r.fTop = offset;
2262 r.fBottom = offset + height;
2263 DrawRect(draw, paint, r, textSize);
2264 }
2265 }
2266}
2267
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268void SkCanvas::drawText(const void* text, size_t byteLength,
2269 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002270 CHECK_SHADER_NOSETCONTEXT(paint);
2271
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002272 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273
2274 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002275 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002276 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002277 DrawTextDecorations(iter, dfp.paint(),
2278 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 }
2280
reed@google.com4e2b3d32011-04-07 14:18:59 +00002281 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282}
2283
2284void SkCanvas::drawPosText(const void* text, size_t byteLength,
2285 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002286 CHECK_SHADER_NOSETCONTEXT(paint);
2287
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002288 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002289
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002291 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002293 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294 }
reed@google.com4b226022011-01-11 18:32:13 +00002295
reed@google.com4e2b3d32011-04-07 14:18:59 +00002296 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297}
2298
2299void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2300 const SkScalar xpos[], SkScalar constY,
2301 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002302 CHECK_SHADER_NOSETCONTEXT(paint);
2303
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002304 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002305
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002307 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002308 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002309 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310 }
reed@google.com4b226022011-01-11 18:32:13 +00002311
reed@google.com4e2b3d32011-04-07 14:18:59 +00002312 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313}
2314
2315void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2316 const SkPath& path, const SkMatrix* matrix,
2317 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002318 CHECK_SHADER_NOSETCONTEXT(paint);
2319
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002320 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321
2322 while (iter.next()) {
2323 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002324 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325 }
2326
reed@google.com4e2b3d32011-04-07 14:18:59 +00002327 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328}
2329
2330void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2331 const SkPoint verts[], const SkPoint texs[],
2332 const SkColor colors[], SkXfermode* xmode,
2333 const uint16_t indices[], int indexCount,
2334 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002335 CHECK_SHADER_NOSETCONTEXT(paint);
2336
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002337 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002338
reed@android.com8a1c16f2008-12-17 15:59:43 +00002339 while (iter.next()) {
2340 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002341 colors, xmode, indices, indexCount,
2342 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343 }
reed@google.com4b226022011-01-11 18:32:13 +00002344
reed@google.com4e2b3d32011-04-07 14:18:59 +00002345 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002346}
2347
2348//////////////////////////////////////////////////////////////////////////////
2349// These methods are NOT virtual, and therefore must call back into virtual
2350// methods, rather than actually drawing themselves.
2351//////////////////////////////////////////////////////////////////////////////
2352
2353void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002354 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002355 SkPaint paint;
2356
2357 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002358 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002359 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360 }
2361 this->drawPaint(paint);
2362}
2363
reed@android.com845fdac2009-06-23 03:01:32 +00002364void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 SkPaint paint;
2366
2367 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002368 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002369 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 }
2371 this->drawPaint(paint);
2372}
2373
2374void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2375 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002376
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377 pt.set(x, y);
2378 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2379}
2380
2381void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2382 SkPoint pt;
2383 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002384
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 pt.set(x, y);
2386 paint.setColor(color);
2387 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2388}
2389
2390void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2391 const SkPaint& paint) {
2392 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002393
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 pts[0].set(x0, y0);
2395 pts[1].set(x1, y1);
2396 this->drawPoints(kLines_PointMode, 2, pts, paint);
2397}
2398
2399void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2400 SkScalar right, SkScalar bottom,
2401 const SkPaint& paint) {
2402 SkRect r;
2403
2404 r.set(left, top, right, bottom);
2405 this->drawRect(r, paint);
2406}
2407
2408void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2409 const SkPaint& paint) {
2410 if (radius < 0) {
2411 radius = 0;
2412 }
2413
2414 SkRect r;
2415 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002416 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417}
2418
2419void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2420 const SkPaint& paint) {
2421 if (rx > 0 && ry > 0) {
2422 if (paint.canComputeFastBounds()) {
2423 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002424 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002425 return;
2426 }
2427 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002428 SkRRect rrect;
2429 rrect.setRectXY(r, rx, ry);
2430 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 } else {
2432 this->drawRect(r, paint);
2433 }
2434}
2435
reed@android.com8a1c16f2008-12-17 15:59:43 +00002436void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2437 SkScalar sweepAngle, bool useCenter,
2438 const SkPaint& paint) {
2439 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2440 this->drawOval(oval, paint);
2441 } else {
2442 SkPath path;
2443 if (useCenter) {
2444 path.moveTo(oval.centerX(), oval.centerY());
2445 }
2446 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2447 if (useCenter) {
2448 path.close();
2449 }
2450 this->drawPath(path, paint);
2451 }
2452}
2453
2454void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2455 const SkPath& path, SkScalar hOffset,
2456 SkScalar vOffset, const SkPaint& paint) {
2457 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002458
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459 matrix.setTranslate(hOffset, vOffset);
2460 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2461}
2462
reed@android.comf76bacf2009-05-13 14:00:33 +00002463///////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002464void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
2465 SkBaseDevice* device = this->getDevice();
2466 if (NULL != device) {
2467 device->EXPERIMENTAL_optimize(picture);
2468 }
2469}
reed@android.comf76bacf2009-05-13 14:00:33 +00002470
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471void SkCanvas::drawPicture(SkPicture& picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002472 SkBaseDevice* device = this->getTopDevice();
2473 if (NULL != device) {
2474 // Canvas has to first give the device the opportunity to render
2475 // the picture itself.
2476 if (device->EXPERIMENTAL_drawPicture(picture)) {
2477 return; // the device has rendered the entire picture
2478 }
2479 }
2480
reed@android.com8a1c16f2008-12-17 15:59:43 +00002481 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482}
2483
reed@android.com8a1c16f2008-12-17 15:59:43 +00002484///////////////////////////////////////////////////////////////////////////////
2485///////////////////////////////////////////////////////////////////////////////
2486
2487SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002488 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002489
2490 SkASSERT(canvas);
2491
2492 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2493 fDone = !fImpl->next();
2494}
2495
2496SkCanvas::LayerIter::~LayerIter() {
2497 fImpl->~SkDrawIter();
2498}
2499
2500void SkCanvas::LayerIter::next() {
2501 fDone = !fImpl->next();
2502}
2503
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002504SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 return fImpl->getDevice();
2506}
2507
2508const SkMatrix& SkCanvas::LayerIter::matrix() const {
2509 return fImpl->getMatrix();
2510}
2511
2512const SkPaint& SkCanvas::LayerIter::paint() const {
2513 const SkPaint* paint = fImpl->getPaint();
2514 if (NULL == paint) {
2515 paint = &fDefaultPaint;
2516 }
2517 return *paint;
2518}
2519
2520const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2521int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2522int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002523
2524///////////////////////////////////////////////////////////////////////////////
2525
2526SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002527
2528///////////////////////////////////////////////////////////////////////////////
2529
2530static bool supported_for_raster_canvas(const SkImageInfo& info) {
2531 switch (info.alphaType()) {
2532 case kPremul_SkAlphaType:
2533 case kOpaque_SkAlphaType:
2534 break;
2535 default:
2536 return false;
2537 }
2538
2539 switch (info.colorType()) {
2540 case kAlpha_8_SkColorType:
2541 case kRGB_565_SkColorType:
2542 case kPMColor_SkColorType:
2543 break;
2544 default:
2545 return false;
2546 }
2547
2548 return true;
2549}
2550
2551SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2552 if (!supported_for_raster_canvas(info)) {
2553 return NULL;
2554 }
2555
2556 SkBitmap bitmap;
2557 if (!bitmap.allocPixels(info)) {
2558 return NULL;
2559 }
2560
2561 // should this functionality be moved into allocPixels()?
2562 if (!bitmap.info().isOpaque()) {
2563 bitmap.eraseColor(0);
2564 }
2565 return SkNEW_ARGS(SkCanvas, (bitmap));
2566}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002567
2568SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2569 if (!supported_for_raster_canvas(info)) {
2570 return NULL;
2571 }
2572
2573 SkBitmap bitmap;
2574 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2575 return NULL;
2576 }
2577
2578 // should this functionality be moved into allocPixels()?
2579 if (!bitmap.info().isOpaque()) {
2580 bitmap.eraseColor(0);
2581 }
2582 return SkNEW_ARGS(SkCanvas, (bitmap));
2583}
2584
2585