blob: 40862915072bb6f7b9c03c169c6a117e0bf6794d [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkBounder.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
94class AutoCheckNoSetContext {
95public:
96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
97 this->assertNoSetContext(fPaint);
98 }
99 ~AutoCheckNoSetContext() {
100 this->assertNoSetContext(fPaint);
101 }
102
103private:
104 const SkPaint& fPaint;
105
106 void assertNoSetContext(const SkPaint& paint) {
107 SkShader* s = paint.getShader();
108 if (s) {
109 SkASSERT(!s->setContextHasBeenCalled());
110 }
111 }
112};
113
114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
119 #define CHECK_SHADER_NOSETCONTEXT(paint)
120#endif
121
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000122typedef SkTLazy<SkPaint> SkLazyPaint;
123
reed@google.com97af1a62012-08-28 12:19:02 +0000124void SkCanvas::predrawNotify() {
125 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000127 }
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000132/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 The clip/matrix/proc are fields that reflect the top of the save/restore
134 stack. Whenever the canvas changes, it marks a dirty flag, and then before
135 these are used (assuming we're not on a layer) we rebuild these cache
136 values: they reflect the top of the save stack, but translated and clipped
137 by the device's XY offset and bitmap-bounds.
138*/
139struct DeviceCM {
140 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000141 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000142 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000146 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 : fNext(NULL) {
148 if (NULL != device) {
149 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000150 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@google.com4b226022011-01-11 18:32:13 +0000152 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000156 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000158 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fDevice->unref();
160 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000161 SkDELETE(fPaint);
162 }
reed@google.com4b226022011-01-11 18:32:13 +0000163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
165 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000166 int x = fDevice->getOrigin().x();
167 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 int width = fDevice->width();
169 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if ((x | y) == 0) {
172 fMatrix = &totalMatrix;
173 fClip = totalClip;
174 } else {
175 fMatrixStorage = totalMatrix;
176 fMatrixStorage.postTranslate(SkIntToScalar(-x),
177 SkIntToScalar(-y));
178 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 totalClip.translate(-x, -y, &fClip);
181 }
182
reed@google.com045e62d2011-10-24 12:19:46 +0000183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000186
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkRegion::kDifference_Op);
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#ifdef SK_DEBUG
195 if (!fClip.isEmpty()) {
196 SkIRect deviceR;
197 deviceR.set(0, 0, width, height);
198 SkASSERT(deviceR.contains(fClip.getBounds()));
199 }
200#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000201 }
202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000204 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207/* This is the record we keep for each save/restore level in the stack.
208 Since a level optionally copies the matrix and/or stack, we have pointers
209 for these fields. If the value is copied for this level, the copy is
210 stored in the ...Storage field, and the pointer points to that. If the
211 value is not copied for this level, we ignore ...Storage, and just point
212 at the corresponding value in the previous level in the stack.
213*/
214class SkCanvas::MCRec {
215public:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000216 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
219 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 DeviceCM* fLayer;
222 /* If there are any layers in the stack, this points to the top-most
223 one that is at or below this level in the stack (so we know what
224 bitmap/device to draw into from this level. This value is NOT
225 reference counted, since the real owner is either our fLayer field,
226 or a previous one in a lower level.)
227 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000228 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000230 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if (NULL != prev) {
232 if (flags & SkCanvas::kMatrix_SaveFlag) {
233 fMatrixStorage = *prev->fMatrix;
234 fMatrix = &fMatrixStorage;
235 } else {
236 fMatrix = prev->fMatrix;
237 }
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000240 fRasterClipStorage = *prev->fRasterClip;
241 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 } else {
reed@google.com00177082011-10-12 14:34:30 +0000243 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
245
246 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000247 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 fTopLayer = prev->fTopLayer;
250 } else { // no prev
251 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000254 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = NULL;
256 fTopLayer = NULL;
257 }
258 fLayer = NULL;
259
260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000264 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDELETE(fLayer);
266 dec_rec();
267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
reed@google.com00177082011-10-12 14:34:30 +0000270 SkMatrix fMatrixStorage;
271 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
274class SkDrawIter : public SkDraw {
275public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000277 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000278 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 canvas->updateDeviceCMCache();
280
reed@google.com90c07ea2012-04-13 13:50:27 +0000281 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fBounder = canvas->getBounder();
283 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000284 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com4b226022011-01-11 18:32:13 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 bool next() {
288 // skip over recs with empty clips
289 if (fSkipEmptyClips) {
290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
291 fCurrLayer = fCurrLayer->fNext;
292 }
293 }
294
reed@google.comf68c5e22012-02-24 16:38:58 +0000295 const DeviceCM* rec = fCurrLayer;
296 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297
298 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
300 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 fDevice = rec->fDevice;
302 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000304 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 fCurrLayer = rec->fNext;
307 if (fBounder) {
308 fBounder->setClip(fClip);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313 }
314 return false;
315 }
reed@google.com4b226022011-01-11 18:32:13 +0000316
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000317 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000318 int getX() const { return fDevice->getOrigin().x(); }
319 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 const SkMatrix& getMatrix() const { return *fMatrix; }
321 const SkRegion& getClip() const { return *fClip; }
322 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324private:
325 SkCanvas* fCanvas;
326 const DeviceCM* fCurrLayer;
327 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 SkBool8 fSkipEmptyClips;
329
330 typedef SkDraw INHERITED;
331};
332
333/////////////////////////////////////////////////////////////////////////////
334
335class AutoDrawLooper {
336public:
reed@google.com8926b162012-03-23 15:36:36 +0000337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000338 bool skipLayerForImageFilter = false,
339 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000344 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
reed@google.com8926b162012-03-23 15:36:36 +0000347 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
348 SkPaint tmp;
349 tmp.setImageFilter(fOrigPaint.getImageFilter());
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000350 (void)canvas->internalSaveLayer(bounds, &tmp,
commit-bot@chromium.org069a55a2014-03-12 15:08:22 +0000351 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
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
813int SkCanvas::save(SaveFlags flags) {
814 // call shared impl
815 return this->internalSave(flags);
816}
817
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000819#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000821#else
822 return true;
823#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824}
825
junov@chromium.orga907ac32012-02-24 21:54:07 +0000826bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000827 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000828 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000829 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000830 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000831 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000832 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000833
834 if (imageFilter) {
835 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
836 // Filters may grow the bounds beyond the device bounds.
837 op = SkRegion::kReplace_Op;
838 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000839 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 if (NULL != bounds) {
841 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000842
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 this->getTotalMatrix().mapRect(&r, *bounds);
844 r.roundOut(&ir);
845 // early exit if the layer's bounds are clipped out
846 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000847 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000848 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000849 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000850 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 }
852 } else { // no user bounds, so just use the clip
853 ir = clipBounds;
854 }
855
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000856 if (bounds_affects_clip(flags)) {
857 fClipStack.clipDevRect(ir, op);
858 // early exit if the clip is now empty
859 if (!fMCRec->fRasterClip->op(ir, op)) {
860 return false;
861 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000862 }
863
864 if (intersection) {
865 *intersection = ir;
866 }
867 return true;
868}
869
870int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
871 SaveFlags flags) {
commit-bot@chromium.org069a55a2014-03-12 15:08:22 +0000872 return this->internalSaveLayer(bounds, paint, flags, false);
reed@google.com8926b162012-03-23 15:36:36 +0000873}
874
reed@google.com76f10a32014-02-05 15:32:21 +0000875static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000876 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000877 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000878 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000879}
880
reed@google.com8926b162012-03-23 15:36:36 +0000881int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
commit-bot@chromium.org069a55a2014-03-12 15:08:22 +0000882 SaveFlags flags, bool justForImageFilter) {
reed@google.comb93ba452014-03-10 19:47:58 +0000883#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
884 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
885#endif
886
junov@chromium.orga907ac32012-02-24 21:54:07 +0000887 // do this before we create the layer. We don't call the public save() since
888 // that would invoke a possibly overridden virtual
889 int count = this->internalSave(flags);
890
891 fDeviceCMDirty = true;
892
893 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000894 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 return count;
896 }
897
reed@google.comb55deeb2012-01-06 14:43:09 +0000898 // Kill the imagefilter if our device doesn't allow it
899 SkLazyPaint lazyP;
900 if (paint && paint->getImageFilter()) {
901 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000902 if (justForImageFilter) {
903 // early exit if the layer was just for the imageFilter
904 return count;
905 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000906 SkPaint* p = lazyP.set(*paint);
907 p->setImageFilter(NULL);
908 paint = p;
909 }
910 }
911
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000912 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
913 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
914 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000916 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000917 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000918 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000919 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000920 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000921 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000922 if (NULL == device) {
923 SkDebugf("Unable to create device for layer.");
924 return count;
925 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000926
reed@google.com6f8f2922011-03-04 22:27:10 +0000927 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000928 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 device->unref();
930
931 layer->fNext = fMCRec->fTopLayer;
932 fMCRec->fLayer = layer;
933 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
934
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000935 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 return count;
937}
938
939int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
940 SaveFlags flags) {
941 if (0xFF == alpha) {
942 return this->saveLayer(bounds, NULL, flags);
943 } else {
944 SkPaint tmpPaint;
945 tmpPaint.setAlpha(alpha);
946 return this->saveLayer(bounds, &tmpPaint, flags);
947 }
948}
949
950void SkCanvas::restore() {
951 // check for underflow
952 if (fMCStack.count() > 1) {
953 this->internalRestore();
954 }
955}
956
957void SkCanvas::internalRestore() {
958 SkASSERT(fMCStack.count() != 0);
959
960 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000961 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000963 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
964 fClipStack.restore();
965 }
966
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000967 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 DeviceCM* layer = fMCRec->fLayer; // may be null
969 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
970 fMCRec->fLayer = NULL;
971
972 // now do the normal restore()
973 fMCRec->~MCRec(); // balanced in save()
974 fMCStack.pop_back();
975 fMCRec = (MCRec*)fMCStack.back();
976
977 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
978 since if we're being recorded, we don't want to record this (the
979 recorder will have already recorded the restore).
980 */
981 if (NULL != layer) {
982 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000983 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000984 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
985 layer->fPaint);
986 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000988
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000989 SkASSERT(fSaveLayerCount > 0);
990 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 }
992 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000993 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994}
995
996int SkCanvas::getSaveCount() const {
997 return fMCStack.count();
998}
999
1000void SkCanvas::restoreToCount(int count) {
1001 // sanity check
1002 if (count < 1) {
1003 count = 1;
1004 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001005
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001006 int n = this->getSaveCount() - count;
1007 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 this->restore();
1009 }
1010}
1011
reed@google.com7c202932011-12-14 18:48:05 +00001012bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001013 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001014}
1015
reed@google.com76f10a32014-02-05 15:32:21 +00001016SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1017 return this->onNewSurface(info);
1018}
1019
1020SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1021 SkBaseDevice* dev = this->getDevice();
1022 return dev ? dev->newSurface(info) : NULL;
1023}
1024
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001025SkImageInfo SkCanvas::imageInfo() const {
1026 SkBaseDevice* dev = this->getDevice();
1027 if (dev) {
1028 return dev->imageInfo();
1029 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001030 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001031 }
1032}
1033
1034const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1035 return this->onPeekPixels(info, rowBytes);
1036}
1037
1038const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1039 SkBaseDevice* dev = this->getDevice();
1040 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1041}
1042
reed@google.com9c135db2014-03-12 18:28:35 +00001043void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1044 return this->onAccessTopLayerPixels(info, rowBytes);
1045}
1046
1047void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1048 SkBaseDevice* dev = this->getTopDevice();
1049 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1050}
1051
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001052SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1053 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1054 if (NULL == fAddr) {
1055 fInfo = canvas->imageInfo();
1056 if (kUnknown_SkColorType == fInfo.colorType() ||
1057 !fBitmap.allocPixels(fInfo))
1058 {
1059 return; // failure, fAddr is NULL
1060 }
1061 fBitmap.lockPixels();
1062 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1063 return; // failure, fAddr is NULL
1064 }
1065 fAddr = fBitmap.getPixels();
1066 fRowBytes = fBitmap.rowBytes();
1067 }
1068 SkASSERT(fAddr); // success
1069}
1070
1071bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1072 if (fAddr) {
1073 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1074 NULL, NULL);
1075 } else {
1076 bitmap->reset();
1077 return false;
1078 }
1079}
1080
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001081void SkCanvas::onPushCull(const SkRect& cullRect) {
1082 // do nothing. Subclasses may do something
1083}
1084
1085void SkCanvas::onPopCull() {
1086 // do nothing. Subclasses may do something
1087}
1088
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089/////////////////////////////////////////////////////////////////////////////
1090
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001091void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001093 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 return;
1095 }
1096
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001097 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001099 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001101
1102 SkDEBUGCODE(bitmap.validate();)
1103 CHECK_LOCKCOUNT_BALANCE(bitmap);
1104
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001105 SkRect storage;
1106 const SkRect* bounds = NULL;
1107 if (paint && paint->canComputeFastBounds()) {
1108 bitmap.getBounds(&storage);
1109 matrix.mapRect(&storage);
1110 bounds = &paint->computeFastBounds(storage, &storage);
1111 }
1112
1113 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001114
1115 while (iter.next()) {
1116 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1117 }
1118
1119 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120}
1121
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001122void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001123 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 SkPaint tmp;
1125 if (NULL == paint) {
1126 tmp.setDither(true);
1127 paint = &tmp;
1128 }
reed@google.com4b226022011-01-11 18:32:13 +00001129
reed@google.com8926b162012-03-23 15:36:36 +00001130 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001132 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001133 paint = &looper.paint();
1134 SkImageFilter* filter = paint->getImageFilter();
1135 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001136 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001137 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001138 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001139 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001140 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001141 SkMatrix matrix = *iter.fMatrix;
1142 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001143 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001144 SkPaint tmpUnfiltered(*paint);
1145 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001146 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1147 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001148 }
1149 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001150 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001151 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001153 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154}
1155
reed@google.com8926b162012-03-23 15:36:36 +00001156void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1157 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001158 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001159 return;
1160 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001161 SkDEBUGCODE(bitmap.validate();)
1162 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001163
reed@google.com8926b162012-03-23 15:36:36 +00001164 SkPaint tmp;
1165 if (NULL == paint) {
1166 paint = &tmp;
1167 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001168
reed@google.com8926b162012-03-23 15:36:36 +00001169 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001170
reed@google.com8926b162012-03-23 15:36:36 +00001171 while (iter.next()) {
1172 paint = &looper.paint();
1173 SkImageFilter* filter = paint->getImageFilter();
1174 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1175 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001176 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001177 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001178 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001179 SkMatrix matrix = *iter.fMatrix;
1180 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001181 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001182 SkPaint tmpUnfiltered(*paint);
1183 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001184 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001185 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001186 }
1187 } else {
1188 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1189 }
1190 }
1191 LOOPER_END
1192}
1193
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194/////////////////////////////////////////////////////////////////////////////
1195
1196bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1197 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001198 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 return fMCRec->fMatrix->preTranslate(dx, dy);
1200}
1201
1202bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1203 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001204 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 return fMCRec->fMatrix->preScale(sx, sy);
1206}
1207
1208bool SkCanvas::rotate(SkScalar degrees) {
1209 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001210 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 return fMCRec->fMatrix->preRotate(degrees);
1212}
1213
1214bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1215 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001216 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 return fMCRec->fMatrix->preSkew(sx, sy);
1218}
1219
1220bool SkCanvas::concat(const SkMatrix& matrix) {
1221 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001222 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 return fMCRec->fMatrix->preConcat(matrix);
1224}
1225
1226void SkCanvas::setMatrix(const SkMatrix& matrix) {
1227 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001228 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 *fMCRec->fMatrix = matrix;
1230}
1231
1232// this is not virtual, so it must call a virtual method so that subclasses
1233// will see its action
1234void SkCanvas::resetMatrix() {
1235 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001236
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 matrix.reset();
1238 this->setMatrix(matrix);
1239}
1240
1241//////////////////////////////////////////////////////////////////////////////
1242
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001243void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001244 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1245 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001246}
1247
1248void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001249#ifdef SK_ENABLE_CLIP_QUICKREJECT
1250 if (SkRegion::kIntersect_Op == op) {
1251 if (fMCRec->fRasterClip->isEmpty()) {
1252 return false;
1253 }
1254
reed@google.com3b3e8952012-08-16 20:53:31 +00001255 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001256 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001257 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001258
1259 fClipStack.clipEmpty();
1260 return fMCRec->fRasterClip->setEmpty();
1261 }
1262 }
1263#endif
1264
reed@google.com5c3d1472011-02-22 19:12:23 +00001265 AutoValidateClip avc(this);
1266
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001268 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001269 if (!fAllowSoftClip) {
1270 edgeStyle = kHard_ClipEdgeStyle;
1271 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272
1273 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001274 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001275 // the matrix. This means we don't have to a) make a path, and b) tell
1276 // the region code to scan-convert the path, only to discover that it
1277 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
1280 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001281 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1282 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001284 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001285 // and clip against that, since it can handle any matrix. However, to
1286 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1287 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 SkPath path;
1289
1290 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001291 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 }
1293}
1294
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001295static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1296 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001297 // base is used to limit the size (and therefore memory allocation) of the
1298 // region that results from scan converting devPath.
1299 SkRegion base;
1300
reed@google.com819c9212011-02-23 18:56:55 +00001301 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001302 // since we are intersect, we can do better (tighter) with currRgn's
1303 // bounds, than just using the device. However, if currRgn is complex,
1304 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001305 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001306 // FIXME: we should also be able to do this when currClip->isBW(),
1307 // but relaxing the test above triggers GM asserts in
1308 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001309 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001310 } else {
reed@google.com00177082011-10-12 14:34:30 +00001311 base.setRect(currClip->getBounds());
1312 SkRasterClip clip;
1313 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001314 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001315 }
reed@google.com819c9212011-02-23 18:56:55 +00001316 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001317 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001318 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001319 currClip->setEmpty();
1320 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001321 }
1322
junov@chromium.orga907ac32012-02-24 21:54:07 +00001323 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001324
1325 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001326 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001327 } else {
reed@google.com00177082011-10-12 14:34:30 +00001328 SkRasterClip clip;
1329 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001330 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001331 }
1332 }
1333}
1334
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001335void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001337 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001338 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1339 } else {
1340 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001341 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001342}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001343
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001344void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001345 SkRRect transformedRRect;
1346 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1347 AutoValidateClip avc(this);
1348
1349 fDeviceCMDirty = true;
1350 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001351 if (!fAllowSoftClip) {
1352 edgeStyle = kHard_ClipEdgeStyle;
1353 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001354
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001355 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001356
1357 SkPath devPath;
1358 devPath.addRRect(transformedRRect);
1359
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001360 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1361 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001362 }
1363
1364 SkPath path;
1365 path.addRRect(rrect);
1366 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001367 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001368}
1369
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001370void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001371 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1372 SkRect r;
1373 if (!path.isInverseFillType() && path.isRect(&r)) {
1374 this->onClipRect(r, op, edgeStyle);
1375 } else {
1376 this->onClipPath(path, op, edgeStyle);
1377 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001378}
1379
1380void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001381#ifdef SK_ENABLE_CLIP_QUICKREJECT
1382 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1383 if (fMCRec->fRasterClip->isEmpty()) {
1384 return false;
1385 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001386
reed@google.com3b3e8952012-08-16 20:53:31 +00001387 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001388 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001389 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001390
reed@google.comda17f752012-08-16 18:27:05 +00001391 fClipStack.clipEmpty();
1392 return fMCRec->fRasterClip->setEmpty();
1393 }
1394 }
1395#endif
1396
reed@google.com5c3d1472011-02-22 19:12:23 +00001397 AutoValidateClip avc(this);
1398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001400 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001401 if (!fAllowSoftClip) {
1402 edgeStyle = kHard_ClipEdgeStyle;
1403 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404
1405 SkPath devPath;
1406 path.transform(*fMCRec->fMatrix, &devPath);
1407
reed@google.comfe701122011-11-08 19:41:23 +00001408 // Check if the transfomation, or the original path itself
1409 // made us empty. Note this can also happen if we contained NaN
1410 // values. computing the bounds detects this, and will set our
1411 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1412 if (devPath.getBounds().isEmpty()) {
1413 // resetting the path will remove any NaN or other wanky values
1414 // that might upset our scan converter.
1415 devPath.reset();
1416 }
1417
reed@google.com5c3d1472011-02-22 19:12:23 +00001418 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001419 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001420
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001421 if (fAllowSimplifyClip) {
1422 devPath.reset();
1423 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1424 const SkClipStack* clipStack = getClipStack();
1425 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1426 const SkClipStack::Element* element;
1427 while ((element = iter.next())) {
1428 SkClipStack::Element::Type type = element->getType();
1429 if (type == SkClipStack::Element::kEmpty_Type) {
1430 continue;
1431 }
1432 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001433 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001434 SkRegion::Op elementOp = element->getOp();
1435 if (elementOp == SkRegion::kReplace_Op) {
1436 devPath = operand;
1437 } else {
1438 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1439 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001440 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1441 // perhaps we need an API change to avoid this sort of mixed-signals about
1442 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001443 if (element->isAA()) {
1444 edgeStyle = kSoft_ClipEdgeStyle;
1445 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001446 }
1447 op = SkRegion::kReplace_Op;
1448 }
1449
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001450 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001453void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001454 bool inverseFilled) {
1455 // This is for updating the clip conservatively using only bounds
1456 // information.
1457 // Contract:
1458 // The current clip must contain the true clip. The true
1459 // clip is the clip that would have normally been computed
1460 // by calls to clipPath and clipRRect
1461 // Objective:
1462 // Keep the current clip as small as possible without
1463 // breaking the contract, using only clip bounding rectangles
1464 // (for performance).
1465
1466 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1467 // don't have to worry about getting caught in a loop. Thus anywhere
1468 // we call a virtual method, we explicitly prefix it with
1469 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001470
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001471 if (inverseFilled) {
1472 switch (op) {
1473 case SkRegion::kIntersect_Op:
1474 case SkRegion::kDifference_Op:
1475 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001476 // the clip unchanged conservatively respects the contract.
1477 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001478 case SkRegion::kUnion_Op:
1479 case SkRegion::kReplace_Op:
1480 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001481 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001482 // These ops can grow the current clip up to the extents of
1483 // the input clip, which is inverse filled, so we just set
1484 // the current clip to the device bounds.
1485 SkRect deviceBounds;
1486 SkIRect deviceIBounds;
1487 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001488 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001489 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1490 // set the clip in device space
1491 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001492 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001493 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001494 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001495 break;
1496 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001497 default:
1498 SkASSERT(0); // unhandled op?
1499 }
1500 } else {
1501 // Not inverse filled
1502 switch (op) {
1503 case SkRegion::kIntersect_Op:
1504 case SkRegion::kUnion_Op:
1505 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001506 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1507 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001508 case SkRegion::kDifference_Op:
1509 // Difference can only shrink the current clip.
1510 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001511 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001512 case SkRegion::kReverseDifference_Op:
1513 // To reverse, we swap in the bounds with a replace op.
1514 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001515 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1516 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001517 case SkRegion::kXOR_Op:
1518 // Be conservative, based on (A XOR B) always included in (A union B),
1519 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1521 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001522 default:
1523 SkASSERT(0); // unhandled op?
1524 }
1525 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001526}
1527
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001528void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001529 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001530}
1531
1532void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001533 AutoValidateClip avc(this);
1534
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001536 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537
reed@google.com5c3d1472011-02-22 19:12:23 +00001538 // todo: signal fClipStack that we have a region, and therefore (I guess)
1539 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001540 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001541
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001542 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543}
1544
reed@google.com819c9212011-02-23 18:56:55 +00001545#ifdef SK_DEBUG
1546void SkCanvas::validateClip() const {
1547 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001548 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001549 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001550 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001551 return;
1552 }
1553
reed@google.com819c9212011-02-23 18:56:55 +00001554 SkIRect ir;
1555 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001556 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001557
robertphillips@google.com80214e22012-07-20 15:33:18 +00001558 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001559 const SkClipStack::Element* element;
1560 while ((element = iter.next()) != NULL) {
1561 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001562 case SkClipStack::Element::kRect_Type:
1563 element->getRect().round(&ir);
1564 tmpClip.op(ir, element->getOp());
1565 break;
1566 case SkClipStack::Element::kEmpty_Type:
1567 tmpClip.setEmpty();
1568 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001569 default: {
1570 SkPath path;
1571 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001572 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001573 break;
1574 }
reed@google.com819c9212011-02-23 18:56:55 +00001575 }
1576 }
reed@google.com819c9212011-02-23 18:56:55 +00001577}
1578#endif
1579
reed@google.com90c07ea2012-04-13 13:50:27 +00001580void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001581 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001582 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001583
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001584 static const SkRect kEmpty = { 0, 0, 0, 0 };
1585 while ((element = iter.next()) != NULL) {
1586 switch (element->getType()) {
1587 case SkClipStack::Element::kPath_Type:
1588 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1589 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001590 case SkClipStack::Element::kRRect_Type:
1591 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1592 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001593 case SkClipStack::Element::kRect_Type:
1594 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1595 break;
1596 case SkClipStack::Element::kEmpty_Type:
1597 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1598 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001599 }
1600 }
1601}
1602
reed@google.com5c3d1472011-02-22 19:12:23 +00001603///////////////////////////////////////////////////////////////////////////////
1604
reed@google.com754de5f2014-02-24 19:38:20 +00001605bool SkCanvas::isClipEmpty() const {
1606 return fMCRec->fRasterClip->isEmpty();
1607}
1608
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001609bool SkCanvas::isClipRect() const {
1610 return fMCRec->fRasterClip->isRect();
1611}
1612
reed@google.com3b3e8952012-08-16 20:53:31 +00001613bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001614
reed@google.com16078632011-12-06 18:56:37 +00001615 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001616 return true;
1617
reed@google.com00177082011-10-12 14:34:30 +00001618 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 return true;
1620 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621
tomhudson@google.com8d430182011-06-06 19:11:19 +00001622 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001623 SkRect dst;
1624 fMCRec->fMatrix->mapRect(&dst, rect);
1625 SkIRect idst;
1626 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001627 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001628 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001629 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001630
reed@android.coma380ae42009-07-21 01:17:02 +00001631 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001632 // TODO: should we use | instead, or compare all 4 at once?
1633 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001634 return true;
1635 }
reed@google.comc0784db2013-12-13 21:16:12 +00001636 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001637 return true;
1638 }
1639 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641}
1642
reed@google.com3b3e8952012-08-16 20:53:31 +00001643bool SkCanvas::quickReject(const SkPath& path) const {
1644 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645}
1646
reed@google.com3b3e8952012-08-16 20:53:31 +00001647bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001648 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001649 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650 return false;
1651 }
1652
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001653 SkMatrix inverse;
1654 // if we can't invert the CTM, we can't return local clip bounds
1655 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001656 if (bounds) {
1657 bounds->setEmpty();
1658 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001659 return false;
1660 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001662 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001663 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001664 // adjust it outwards in case we are antialiasing
1665 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001666
reed@google.com8f4d2302013-12-17 16:44:46 +00001667 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1668 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669 inverse.mapRect(bounds, r);
1670 }
1671 return true;
1672}
1673
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001674bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001675 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001676 if (clip.isEmpty()) {
1677 if (bounds) {
1678 bounds->setEmpty();
1679 }
1680 return false;
1681 }
1682
1683 if (NULL != bounds) {
1684 *bounds = clip.getBounds();
1685 }
1686 return true;
1687}
1688
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689const SkMatrix& SkCanvas::getTotalMatrix() const {
1690 return *fMCRec->fMatrix;
1691}
1692
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001693#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001694SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001695 if (fMCRec->fRasterClip->isEmpty()) {
1696 return kEmpty_ClipType;
1697 }
1698 if (fMCRec->fRasterClip->isRect()) {
1699 return kRect_ClipType;
1700 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001701 return kComplex_ClipType;
1702}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001703#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001704
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001705#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001707 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001709#endif
1710
1711const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1712 return fMCRec->fRasterClip->forceGetBW();
1713}
1714
1715void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1716 path->reset();
1717
1718 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1719 if (rgn.isEmpty()) {
1720 return;
1721 }
1722 (void)rgn.getBoundaryPath(path);
1723}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724
reed@google.com9c135db2014-03-12 18:28:35 +00001725GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1726 SkBaseDevice* dev = this->getTopDevice();
1727 return dev ? dev->accessRenderTarget() : NULL;
1728}
1729
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001730SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001731 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001732 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733}
1734
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001735GrContext* SkCanvas::getGrContext() {
1736#if SK_SUPPORT_GPU
1737 SkBaseDevice* device = this->getTopDevice();
1738 if (NULL != device) {
1739 GrRenderTarget* renderTarget = device->accessRenderTarget();
1740 if (NULL != renderTarget) {
1741 return renderTarget->getContext();
1742 }
1743 }
1744#endif
1745
1746 return NULL;
1747
1748}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001749
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001750void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1751 const SkPaint& paint) {
1752 if (outer.isEmpty()) {
1753 return;
1754 }
1755 if (inner.isEmpty()) {
1756 this->drawRRect(outer, paint);
1757 return;
1758 }
1759
1760 // We don't have this method (yet), but technically this is what we should
1761 // be able to assert...
1762 // SkASSERT(outer.contains(inner));
1763 //
1764 // For now at least check for containment of bounds
1765 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1766
1767 this->onDrawDRRect(outer, inner, paint);
1768}
1769
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770//////////////////////////////////////////////////////////////////////////////
1771// These are the virtual drawing methods
1772//////////////////////////////////////////////////////////////////////////////
1773
reed@google.com2a981812011-04-14 18:59:28 +00001774void SkCanvas::clear(SkColor color) {
1775 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001776 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001777 while (iter.next()) {
1778 iter.fDevice->clear(color);
1779 }
1780}
1781
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001783 this->internalDrawPaint(paint);
1784}
1785
1786void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001787 CHECK_SHADER_NOSETCONTEXT(paint);
1788
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001789 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790
1791 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001792 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793 }
1794
reed@google.com4e2b3d32011-04-07 14:18:59 +00001795 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796}
1797
1798void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1799 const SkPaint& paint) {
1800 if ((long)count <= 0) {
1801 return;
1802 }
1803
reed@google.comea033602012-12-14 13:13:55 +00001804 CHECK_SHADER_NOSETCONTEXT(paint);
1805
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001806 SkRect r, storage;
1807 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001808 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001809 // special-case 2 points (common for drawing a single line)
1810 if (2 == count) {
1811 r.set(pts[0], pts[1]);
1812 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001813 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001814 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001815 bounds = &paint.computeFastStrokeBounds(r, &storage);
1816 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001817 return;
1818 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001819 }
reed@google.coma584aed2012-05-16 14:06:02 +00001820
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821 SkASSERT(pts != NULL);
1822
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001824
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001826 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 }
reed@google.com4b226022011-01-11 18:32:13 +00001828
reed@google.com4e2b3d32011-04-07 14:18:59 +00001829 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830}
1831
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001832void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001833 CHECK_SHADER_NOSETCONTEXT(paint);
1834
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001835 SkRect storage;
1836 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001838 bounds = &paint.computeFastBounds(r, &storage);
1839 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840 return;
1841 }
1842 }
reed@google.com4b226022011-01-11 18:32:13 +00001843
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001844 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845
1846 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001847 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848 }
1849
reed@google.com4e2b3d32011-04-07 14:18:59 +00001850 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851}
1852
reed@google.com4ed0fb72012-12-12 20:48:18 +00001853void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001854 CHECK_SHADER_NOSETCONTEXT(paint);
1855
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001856 SkRect storage;
1857 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001858 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001859 bounds = &paint.computeFastBounds(oval, &storage);
1860 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001861 return;
1862 }
1863 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001864
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001865 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001866
1867 while (iter.next()) {
1868 iter.fDevice->drawOval(iter, oval, looper.paint());
1869 }
1870
1871 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001872}
1873
1874void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001875 CHECK_SHADER_NOSETCONTEXT(paint);
1876
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001877 SkRect storage;
1878 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001879 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001880 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1881 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001882 return;
1883 }
1884 }
1885
1886 if (rrect.isRect()) {
1887 // call the non-virtual version
1888 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001889 return;
1890 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001891 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001892 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1893 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001894 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001895
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001896 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001897
1898 while (iter.next()) {
1899 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1900 }
1901
1902 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001903}
1904
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001905void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1906 const SkPaint& paint) {
1907 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001908
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001909 SkRect storage;
1910 const SkRect* bounds = NULL;
1911 if (paint.canComputeFastBounds()) {
1912 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1913 if (this->quickReject(*bounds)) {
1914 return;
1915 }
1916 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001917
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001918 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001919
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001920 while (iter.next()) {
1921 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1922 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001923
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001924 LOOPER_END
1925}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001926
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001927void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001928 CHECK_SHADER_NOSETCONTEXT(paint);
1929
reed@google.com93645112012-07-26 16:11:47 +00001930 if (!path.isFinite()) {
1931 return;
1932 }
1933
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001934 SkRect storage;
1935 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001936 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001937 const SkRect& pathBounds = path.getBounds();
1938 bounds = &paint.computeFastBounds(pathBounds, &storage);
1939 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940 return;
1941 }
1942 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001943
1944 const SkRect& r = path.getBounds();
1945 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001946 if (path.isInverseFillType()) {
1947 this->internalDrawPaint(paint);
1948 }
1949 return;
1950 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001952 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953
1954 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001955 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956 }
1957
reed@google.com4e2b3d32011-04-07 14:18:59 +00001958 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959}
1960
1961void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1962 const SkPaint* paint) {
1963 SkDEBUGCODE(bitmap.validate();)
1964
reed@google.com3d608122011-11-21 15:16:16 +00001965 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001966 SkRect bounds = {
1967 x, y,
1968 x + SkIntToScalar(bitmap.width()),
1969 y + SkIntToScalar(bitmap.height())
1970 };
1971 if (paint) {
1972 (void)paint->computeFastBounds(bounds, &bounds);
1973 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001974 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975 return;
1976 }
1977 }
reed@google.com4b226022011-01-11 18:32:13 +00001978
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 SkMatrix matrix;
1980 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001981 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982}
1983
reed@google.com9987ec32011-09-07 11:57:52 +00001984// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001985void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001986 const SkRect& dst, const SkPaint* paint,
1987 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001988 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001989 return;
1990 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001991
reed@google.comea033602012-12-14 13:13:55 +00001992 CHECK_LOCKCOUNT_BALANCE(bitmap);
1993
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001994 SkRect storage;
1995 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001996 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001997 if (paint) {
1998 bounds = &paint->computeFastBounds(dst, &storage);
1999 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002000 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002001 return;
2002 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003 }
reed@google.com3d608122011-11-21 15:16:16 +00002004
reed@google.com33535f32012-09-25 15:37:50 +00002005 SkLazyPaint lazy;
2006 if (NULL == paint) {
2007 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002009
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002010 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002011
reed@google.com33535f32012-09-25 15:37:50 +00002012 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002013 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002014 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002015
reed@google.com33535f32012-09-25 15:37:50 +00002016 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002017}
2018
reed@google.com71121732012-09-18 15:14:33 +00002019void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002020 const SkRect& dst, const SkPaint* paint,
2021 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002022 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002023 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002024}
2025
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2027 const SkPaint* paint) {
2028 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002029 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030}
2031
reed@google.com9987ec32011-09-07 11:57:52 +00002032void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2033 const SkIRect& center, const SkRect& dst,
2034 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002035 if (bitmap.drawsNothing()) {
2036 return;
2037 }
reed@google.com3d608122011-11-21 15:16:16 +00002038 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002039 SkRect storage;
2040 const SkRect* bounds = &dst;
2041 if (paint) {
2042 bounds = &paint->computeFastBounds(dst, &storage);
2043 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002044 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002045 return;
2046 }
2047 }
2048
reed@google.com9987ec32011-09-07 11:57:52 +00002049 const int32_t w = bitmap.width();
2050 const int32_t h = bitmap.height();
2051
2052 SkIRect c = center;
2053 // pin center to the bounds of the bitmap
2054 c.fLeft = SkMax32(0, center.fLeft);
2055 c.fTop = SkMax32(0, center.fTop);
2056 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2057 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2058
reed@google.com71121732012-09-18 15:14:33 +00002059 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002060 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002061 };
2062 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002063 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002064 };
reed@google.com9987ec32011-09-07 11:57:52 +00002065 SkScalar dstX[4] = {
2066 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2067 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2068 };
2069 SkScalar dstY[4] = {
2070 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2071 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2072 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002073
reed@google.com9987ec32011-09-07 11:57:52 +00002074 if (dstX[1] > dstX[2]) {
2075 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2076 dstX[2] = dstX[1];
2077 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002078
reed@google.com9987ec32011-09-07 11:57:52 +00002079 if (dstY[1] > dstY[2]) {
2080 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2081 dstY[2] = dstY[1];
2082 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002083
reed@google.com9987ec32011-09-07 11:57:52 +00002084 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002085 SkRect s, d;
2086
reed@google.com9987ec32011-09-07 11:57:52 +00002087 s.fTop = srcY[y];
2088 s.fBottom = srcY[y+1];
2089 d.fTop = dstY[y];
2090 d.fBottom = dstY[y+1];
2091 for (int x = 0; x < 3; x++) {
2092 s.fLeft = srcX[x];
2093 s.fRight = srcX[x+1];
2094 d.fLeft = dstX[x];
2095 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002096 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002097 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002098 }
2099 }
2100}
2101
2102void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2103 const SkRect& dst, const SkPaint* paint) {
2104 SkDEBUGCODE(bitmap.validate();)
2105
2106 // Need a device entry-point, so gpu can use a mesh
2107 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2108}
2109
reed@google.comf67e4cf2011-03-15 20:56:58 +00002110class SkDeviceFilteredPaint {
2111public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002112 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2113 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002114 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002115 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002116 newPaint->setFlags(flags.fFlags);
2117 newPaint->setHinting(flags.fHinting);
2118 fPaint = newPaint;
2119 } else {
2120 fPaint = &paint;
2121 }
2122 }
2123
reed@google.comf67e4cf2011-03-15 20:56:58 +00002124 const SkPaint& paint() const { return *fPaint; }
2125
2126private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002127 const SkPaint* fPaint;
2128 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002129};
2130
bungeman@google.com52c748b2011-08-22 21:30:43 +00002131void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2132 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002133 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002134 draw.fDevice->drawRect(draw, r, paint);
2135 } else {
2136 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002137 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002138 draw.fDevice->drawRect(draw, r, p);
2139 }
2140}
2141
2142void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2143 const char text[], size_t byteLength,
2144 SkScalar x, SkScalar y) {
2145 SkASSERT(byteLength == 0 || text != NULL);
2146
2147 // nothing to draw
2148 if (text == NULL || byteLength == 0 ||
2149 draw.fClip->isEmpty() ||
2150 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2151 return;
2152 }
2153
2154 SkScalar width = 0;
2155 SkPoint start;
2156
2157 start.set(0, 0); // to avoid warning
2158 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2159 SkPaint::kStrikeThruText_Flag)) {
2160 width = paint.measureText(text, byteLength);
2161
2162 SkScalar offsetX = 0;
2163 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2164 offsetX = SkScalarHalf(width);
2165 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2166 offsetX = width;
2167 }
2168 start.set(x - offsetX, y);
2169 }
2170
2171 if (0 == width) {
2172 return;
2173 }
2174
2175 uint32_t flags = paint.getFlags();
2176
2177 if (flags & (SkPaint::kUnderlineText_Flag |
2178 SkPaint::kStrikeThruText_Flag)) {
2179 SkScalar textSize = paint.getTextSize();
2180 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2181 SkRect r;
2182
2183 r.fLeft = start.fX;
2184 r.fRight = start.fX + width;
2185
2186 if (flags & SkPaint::kUnderlineText_Flag) {
2187 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2188 start.fY);
2189 r.fTop = offset;
2190 r.fBottom = offset + height;
2191 DrawRect(draw, paint, r, textSize);
2192 }
2193 if (flags & SkPaint::kStrikeThruText_Flag) {
2194 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2195 start.fY);
2196 r.fTop = offset;
2197 r.fBottom = offset + height;
2198 DrawRect(draw, paint, r, textSize);
2199 }
2200 }
2201}
2202
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203void SkCanvas::drawText(const void* text, size_t byteLength,
2204 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002205 CHECK_SHADER_NOSETCONTEXT(paint);
2206
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002207 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208
2209 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002210 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002211 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002212 DrawTextDecorations(iter, dfp.paint(),
2213 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214 }
2215
reed@google.com4e2b3d32011-04-07 14:18:59 +00002216 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217}
2218
2219void SkCanvas::drawPosText(const void* text, size_t byteLength,
2220 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002221 CHECK_SHADER_NOSETCONTEXT(paint);
2222
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002223 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002224
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002226 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002228 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002229 }
reed@google.com4b226022011-01-11 18:32:13 +00002230
reed@google.com4e2b3d32011-04-07 14:18:59 +00002231 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232}
2233
2234void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2235 const SkScalar xpos[], SkScalar constY,
2236 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002237 CHECK_SHADER_NOSETCONTEXT(paint);
2238
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002239 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002240
reed@android.com8a1c16f2008-12-17 15:59:43 +00002241 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002242 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002244 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 }
reed@google.com4b226022011-01-11 18:32:13 +00002246
reed@google.com4e2b3d32011-04-07 14:18:59 +00002247 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248}
2249
2250void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2251 const SkPath& path, const SkMatrix* matrix,
2252 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002253 CHECK_SHADER_NOSETCONTEXT(paint);
2254
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002255 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256
2257 while (iter.next()) {
2258 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002259 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 }
2261
reed@google.com4e2b3d32011-04-07 14:18:59 +00002262 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263}
2264
2265void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2266 const SkPoint verts[], const SkPoint texs[],
2267 const SkColor colors[], SkXfermode* xmode,
2268 const uint16_t indices[], int indexCount,
2269 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::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002273
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274 while (iter.next()) {
2275 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002276 colors, xmode, indices, indexCount,
2277 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278 }
reed@google.com4b226022011-01-11 18:32:13 +00002279
reed@google.com4e2b3d32011-04-07 14:18:59 +00002280 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281}
2282
2283//////////////////////////////////////////////////////////////////////////////
2284// These methods are NOT virtual, and therefore must call back into virtual
2285// methods, rather than actually drawing themselves.
2286//////////////////////////////////////////////////////////////////////////////
2287
2288void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002289 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 SkPaint paint;
2291
2292 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002293 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002294 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 }
2296 this->drawPaint(paint);
2297}
2298
reed@android.com845fdac2009-06-23 03:01:32 +00002299void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 SkPaint paint;
2301
2302 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002303 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002304 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 }
2306 this->drawPaint(paint);
2307}
2308
2309void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2310 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002311
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312 pt.set(x, y);
2313 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2314}
2315
2316void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2317 SkPoint pt;
2318 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002319
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 pt.set(x, y);
2321 paint.setColor(color);
2322 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2323}
2324
2325void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2326 const SkPaint& paint) {
2327 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002328
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329 pts[0].set(x0, y0);
2330 pts[1].set(x1, y1);
2331 this->drawPoints(kLines_PointMode, 2, pts, paint);
2332}
2333
2334void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2335 SkScalar right, SkScalar bottom,
2336 const SkPaint& paint) {
2337 SkRect r;
2338
2339 r.set(left, top, right, bottom);
2340 this->drawRect(r, paint);
2341}
2342
2343void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2344 const SkPaint& paint) {
2345 if (radius < 0) {
2346 radius = 0;
2347 }
2348
2349 SkRect r;
2350 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002351 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352}
2353
2354void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2355 const SkPaint& paint) {
2356 if (rx > 0 && ry > 0) {
2357 if (paint.canComputeFastBounds()) {
2358 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002359 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360 return;
2361 }
2362 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002363 SkRRect rrect;
2364 rrect.setRectXY(r, rx, ry);
2365 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 } else {
2367 this->drawRect(r, paint);
2368 }
2369}
2370
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2372 SkScalar sweepAngle, bool useCenter,
2373 const SkPaint& paint) {
2374 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2375 this->drawOval(oval, paint);
2376 } else {
2377 SkPath path;
2378 if (useCenter) {
2379 path.moveTo(oval.centerX(), oval.centerY());
2380 }
2381 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2382 if (useCenter) {
2383 path.close();
2384 }
2385 this->drawPath(path, paint);
2386 }
2387}
2388
2389void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2390 const SkPath& path, SkScalar hOffset,
2391 SkScalar vOffset, const SkPaint& paint) {
2392 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002393
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 matrix.setTranslate(hOffset, vOffset);
2395 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2396}
2397
reed@android.comf76bacf2009-05-13 14:00:33 +00002398///////////////////////////////////////////////////////////////////////////////
2399
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402}
2403
reed@android.com8a1c16f2008-12-17 15:59:43 +00002404///////////////////////////////////////////////////////////////////////////////
2405///////////////////////////////////////////////////////////////////////////////
2406
2407SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002408 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409
2410 SkASSERT(canvas);
2411
2412 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2413 fDone = !fImpl->next();
2414}
2415
2416SkCanvas::LayerIter::~LayerIter() {
2417 fImpl->~SkDrawIter();
2418}
2419
2420void SkCanvas::LayerIter::next() {
2421 fDone = !fImpl->next();
2422}
2423
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002424SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002425 return fImpl->getDevice();
2426}
2427
2428const SkMatrix& SkCanvas::LayerIter::matrix() const {
2429 return fImpl->getMatrix();
2430}
2431
2432const SkPaint& SkCanvas::LayerIter::paint() const {
2433 const SkPaint* paint = fImpl->getPaint();
2434 if (NULL == paint) {
2435 paint = &fDefaultPaint;
2436 }
2437 return *paint;
2438}
2439
2440const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2441int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2442int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002443
2444///////////////////////////////////////////////////////////////////////////////
2445
2446SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002447
2448///////////////////////////////////////////////////////////////////////////////
2449
2450static bool supported_for_raster_canvas(const SkImageInfo& info) {
2451 switch (info.alphaType()) {
2452 case kPremul_SkAlphaType:
2453 case kOpaque_SkAlphaType:
2454 break;
2455 default:
2456 return false;
2457 }
2458
2459 switch (info.colorType()) {
2460 case kAlpha_8_SkColorType:
2461 case kRGB_565_SkColorType:
2462 case kPMColor_SkColorType:
2463 break;
2464 default:
2465 return false;
2466 }
2467
2468 return true;
2469}
2470
2471SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2472 if (!supported_for_raster_canvas(info)) {
2473 return NULL;
2474 }
2475
2476 SkBitmap bitmap;
2477 if (!bitmap.allocPixels(info)) {
2478 return NULL;
2479 }
2480
2481 // should this functionality be moved into allocPixels()?
2482 if (!bitmap.info().isOpaque()) {
2483 bitmap.eraseColor(0);
2484 }
2485 return SkNEW_ARGS(SkCanvas, (bitmap));
2486}