blob: 96d16fcdb93393cca19ec210c9b7ca577f21dc1a [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"
reed@google.com97af1a62012-08-28 12:19:02 +000022#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000024#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000025#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000028#if SK_SUPPORT_GPU
29#include "GrRenderTarget.h"
30#endif
31
reed@google.comda17f752012-08-16 18:27:05 +000032// experimental for faster tiled drawing...
33//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000034
reed@android.com8a1c16f2008-12-17 15:59:43 +000035//#define SK_TRACE_SAVERESTORE
36
37#ifdef SK_TRACE_SAVERESTORE
38 static int gLayerCounter;
39 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
40 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
41
42 static int gRecCounter;
43 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
44 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
45
46 static int gCanvasCounter;
47 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
48 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
49#else
50 #define inc_layer()
51 #define dec_layer()
52 #define inc_rec()
53 #define dec_rec()
54 #define inc_canvas()
55 #define dec_canvas()
56#endif
57
reed@google.comea033602012-12-14 13:13:55 +000058#ifdef SK_DEBUG
59#include "SkPixelRef.h"
60
reed@google.comf53d0a92013-01-30 13:17:32 +000061/*
62 * Some pixelref subclasses can support being "locked" from another thread
63 * during the lock-scope of skia calling them. In these instances, this balance
64 * check will fail, but may not be indicative of a problem, so we allow a build
65 * flag to disable this check.
66 *
67 * Potentially another fix would be to have a (debug-only) virtual or flag on
68 * pixelref, which could tell us at runtime if this check is valid. That would
69 * eliminate the need for this heavy-handed build check.
70 */
71#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
72class AutoCheckLockCountBalance {
73public:
74 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
75};
76#else
reed@google.comea033602012-12-14 13:13:55 +000077class AutoCheckLockCountBalance {
78public:
79 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
80 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
81 }
82 ~AutoCheckLockCountBalance() {
83 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
84 SkASSERT(count == fLockCount);
85 }
86
87private:
88 const SkPixelRef* fPixelRef;
89 int fLockCount;
90};
reed@google.comf53d0a92013-01-30 13:17:32 +000091#endif
reed@google.comea033602012-12-14 13:13:55 +000092
93class AutoCheckNoSetContext {
94public:
95 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
96 this->assertNoSetContext(fPaint);
97 }
98 ~AutoCheckNoSetContext() {
99 this->assertNoSetContext(fPaint);
100 }
101
102private:
103 const SkPaint& fPaint;
104
105 void assertNoSetContext(const SkPaint& paint) {
106 SkShader* s = paint.getShader();
107 if (s) {
108 SkASSERT(!s->setContextHasBeenCalled());
109 }
110 }
111};
112
113#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
114#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
115
116#else
117 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
118 #define CHECK_SHADER_NOSETCONTEXT(paint)
119#endif
120
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000121typedef SkTLazy<SkPaint> SkLazyPaint;
122
reed@google.com97af1a62012-08-28 12:19:02 +0000123void SkCanvas::predrawNotify() {
124 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000125 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000126 }
127}
128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000131/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 The clip/matrix/proc are fields that reflect the top of the save/restore
133 stack. Whenever the canvas changes, it marks a dirty flag, and then before
134 these are used (assuming we're not on a layer) we rebuild these cache
135 values: they reflect the top of the save stack, but translated and clipped
136 by the device's XY offset and bitmap-bounds.
137*/
138struct DeviceCM {
139 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000140 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000141 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000143 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000145 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 : fNext(NULL) {
147 if (NULL != device) {
148 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000149 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 }
reed@google.com4b226022011-01-11 18:32:13 +0000151 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000153 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000155 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000157 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 fDevice->unref();
159 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000160 SkDELETE(fPaint);
161 }
reed@google.com4b226022011-01-11 18:32:13 +0000162
reed@google.com045e62d2011-10-24 12:19:46 +0000163 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
164 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000165 int x = fDevice->getOrigin().x();
166 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 int width = fDevice->width();
168 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 if ((x | y) == 0) {
171 fMatrix = &totalMatrix;
172 fClip = totalClip;
173 } else {
174 fMatrixStorage = totalMatrix;
175 fMatrixStorage.postTranslate(SkIntToScalar(-x),
176 SkIntToScalar(-y));
177 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 totalClip.translate(-x, -y, &fClip);
180 }
181
reed@google.com045e62d2011-10-24 12:19:46 +0000182 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
184 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000187 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 SkRegion::kDifference_Op);
189 }
reed@google.com4b226022011-01-11 18:32:13 +0000190
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000191 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193#ifdef SK_DEBUG
194 if (!fClip.isEmpty()) {
195 SkIRect deviceR;
196 deviceR.set(0, 0, width, height);
197 SkASSERT(deviceR.contains(fClip.getBounds()));
198 }
199#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000200 }
201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000203 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204};
205
206/* This is the record we keep for each save/restore level in the stack.
207 Since a level optionally copies the matrix and/or stack, we have pointers
208 for these fields. If the value is copied for this level, the copy is
209 stored in the ...Storage field, and the pointer points to that. If the
210 value is not copied for this level, we ignore ...Storage, and just point
211 at the corresponding value in the previous level in the stack.
212*/
213class SkCanvas::MCRec {
214public:
215 MCRec* fNext;
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;
341 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000343 fPaint = NULL;
344 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000345 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000346 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
reed@google.com8926b162012-03-23 15:36:36 +0000348 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
349 SkPaint tmp;
350 tmp.setImageFilter(fOrigPaint.getImageFilter());
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000351 (void)canvas->internalSaveLayer(bounds, &tmp,
reed@google.com8926b162012-03-23 15:36:36 +0000352 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
353 // we'll clear the imageFilter for the actual draws in next(), so
354 // it will only be applied during the restore().
355 fDoClearImageFilter = true;
356 }
357
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 if (fLooper) {
359 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000360 fIsSimple = false;
361 } else {
362 // can we be marked as simple?
363 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364 }
365 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000368 if (fDoClearImageFilter) {
369 fCanvas->internalRestore();
370 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000371 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000373
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 const SkPaint& paint() const {
375 SkASSERT(fPaint);
376 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000378
reed@google.com129ec222012-05-15 13:24:09 +0000379 bool next(SkDrawFilter::Type drawType) {
380 if (fDone) {
381 return false;
382 } else if (fIsSimple) {
383 fDone = true;
384 fPaint = &fOrigPaint;
385 return !fPaint->nothingToDraw();
386 } else {
387 return this->doNext(drawType);
388 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000389 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000392 SkLazyPaint fLazyPaint;
393 SkCanvas* fCanvas;
394 const SkPaint& fOrigPaint;
395 SkDrawLooper* fLooper;
396 SkDrawFilter* fFilter;
397 const SkPaint* fPaint;
398 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000399 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000400 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000401 bool fIsSimple;
402
403 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404};
405
reed@google.com129ec222012-05-15 13:24:09 +0000406bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000407 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000408 SkASSERT(!fIsSimple);
409 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
410
411 SkPaint* paint = fLazyPaint.set(fOrigPaint);
412
413 if (fDoClearImageFilter) {
414 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416
reed@google.com129ec222012-05-15 13:24:09 +0000417 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000419 return false;
420 }
421 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000422 if (!fFilter->filter(paint, drawType)) {
423 fDone = true;
424 return false;
425 }
reed@google.com129ec222012-05-15 13:24:09 +0000426 if (NULL == fLooper) {
427 // no looper means we only draw once
428 fDone = true;
429 }
430 }
431 fPaint = paint;
432
433 // if we only came in here for the imagefilter, mark us as done
434 if (!fLooper && !fFilter) {
435 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000436 }
437
438 // call this after any possible paint modifiers
439 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440 fPaint = NULL;
441 return false;
442 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 return true;
444}
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446/* Stack helper for managing a SkBounder. In the destructor, if we were
447 given a bounder, we call its commit() method, signifying that we are
448 done accumulating bounds for that draw.
449*/
450class SkAutoBounderCommit {
451public:
452 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
453 ~SkAutoBounderCommit() {
454 if (NULL != fBounder) {
455 fBounder->commit();
456 }
457 }
458private:
459 SkBounder* fBounder;
460};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000461#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462
463#include "SkColorPriv.h"
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465////////// macros to place around the internal draw calls //////////////////
466
reed@google.com8926b162012-03-23 15:36:36 +0000467#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000468 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000469 AutoDrawLooper looper(this, paint, true); \
470 while (looper.next(type)) { \
471 SkAutoBounderCommit ac(fBounder); \
472 SkDrawIter iter(this);
473
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000474#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000475 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000476 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000477 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkAutoBounderCommit ac(fBounder); \
479 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000480
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482
483////////////////////////////////////////////////////////////////////////////
484
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000485SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000487 fCachedLocalClipBounds.setEmpty();
488 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000489 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000490 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000491 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000492 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000493 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000494 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495
496 fMCRec = (MCRec*)fMCStack.push_back();
497 new (fMCRec) MCRec(NULL, 0);
498
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000499 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 fMCRec->fTopLayer = fMCRec->fLayer;
501 fMCRec->fNext = NULL;
502
reed@google.com97af1a62012-08-28 12:19:02 +0000503 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000504
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000505 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506}
507
reed@google.comcde92112011-07-06 20:00:52 +0000508SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000509 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
510{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000511 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000512
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000513 this->init(NULL);
514}
515
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000516SkCanvas::SkCanvas(int width, int height)
517 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
518{
519 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000520
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000521 SkBitmap bitmap;
reed@google.com900ecf22014-02-20 20:55:37 +0000522 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000523 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
524}
525
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000526SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000527 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
528{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 inc_canvas();
530
531 this->init(device);
532}
533
534SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
536{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 inc_canvas();
538
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000539 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540}
541
542SkCanvas::~SkCanvas() {
543 // free up the contents of our deque
544 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000545 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000546
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 this->internalRestore(); // restore the last, since we're going away
548
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000549 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000550 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000551
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 dec_canvas();
553}
554
555SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
556 SkRefCnt_SafeAssign(fBounder, bounder);
557 return bounder;
558}
559
560SkDrawFilter* SkCanvas::getDrawFilter() const {
561 return fMCRec->fFilter;
562}
563
564SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
565 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
566 return filter;
567}
568
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000569SkMetaData& SkCanvas::getMetaData() {
570 // metadata users are rare, so we lazily allocate it. If that changes we
571 // can decide to just make it a field in the device (rather than a ptr)
572 if (NULL == fMetaData) {
573 fMetaData = new SkMetaData;
574 }
575 return *fMetaData;
576}
577
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578///////////////////////////////////////////////////////////////////////////////
579
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000580void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000581 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000582 if (device) {
583 device->flush();
584 }
585}
586
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000587SkISize SkCanvas::getTopLayerSize() const {
588 SkBaseDevice* d = this->getTopDevice();
589 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
590}
591
592SkIPoint SkCanvas::getTopLayerOrigin() const {
593 SkBaseDevice* d = this->getTopDevice();
594 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
595}
596
597SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000598 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000599 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
600}
601
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000602SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000604 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkASSERT(rec && rec->fLayer);
606 return rec->fLayer->fDevice;
607}
608
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000609SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000610 if (updateMatrixClip) {
611 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
612 }
reed@google.com9266fed2011-03-30 00:18:03 +0000613 return fMCRec->fTopLayer->fDevice;
614}
615
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000616SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000618 SkDeque::F2BIter iter(fMCStack);
619 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000621 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622
623 if (rootDevice == device) {
624 return device;
625 }
reed@google.com4b226022011-01-11 18:32:13 +0000626
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000628 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 }
630 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000631 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 }
633
634 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
635 rootDevice = device;
636
637 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 /* Now we update our initial region to have the bounds of the new device,
640 and then intersect all of the clips in our stack with these bounds,
641 to ensure that we can't draw outside of the device's bounds (and trash
642 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000643
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 NOTE: this is only a partial-fix, since if the new device is larger than
645 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000646 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
648 reconstruct the correct clips, so this approximation will have to do.
649 The caller really needs to restore() back to the base if they want to
650 accurately take advantage of the new device bounds.
651 */
652
reed@google.com42aea282012-03-28 16:19:15 +0000653 SkIRect bounds;
654 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000656 } else {
657 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 }
reed@google.com42aea282012-03-28 16:19:15 +0000659 // now jam our 1st clip to be bounds, and intersect the rest with that
660 rec->fRasterClip->setRect(bounds);
661 while ((rec = (MCRec*)iter.next()) != NULL) {
662 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
663 }
664
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 return device;
666}
667
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000668bool SkCanvas::readPixels(SkBitmap* bitmap,
669 int x, int y,
670 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000671 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000672 if (!device) {
673 return false;
674 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000675 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000676}
677
bsalomon@google.comc6980972011-11-02 19:57:21 +0000678bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000679 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000680 if (!device) {
681 return false;
682 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000683
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000684 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000685 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000686 if (!bounds.intersect(srcRect)) {
687 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000688 }
689
690 SkBitmap tmp;
691 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
692 bounds.height());
693 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
694 bitmap->swap(tmp);
695 return true;
696 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000697 return false;
698 }
reed@google.com51df9e32010-12-23 19:29:18 +0000699}
700
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000701void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
702 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000703 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000704 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000705 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
706 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
707 device->accessBitmap(true);
708 device->writePixels(bitmap, x, y, config8888);
709 }
reed@google.com51df9e32010-12-23 19:29:18 +0000710 }
711}
712
junov@google.com4370aed2012-01-18 16:21:08 +0000713SkCanvas* SkCanvas::canvasForDrawIter() {
714 return this;
715}
716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717//////////////////////////////////////////////////////////////////////////////
718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719void SkCanvas::updateDeviceCMCache() {
720 if (fDeviceCMDirty) {
721 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000722 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000724
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000726 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000728 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000730 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 } while ((layer = layer->fNext) != NULL);
732 }
733 fDeviceCMDirty = false;
734 }
735}
736
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737///////////////////////////////////////////////////////////////////////////////
738
739int SkCanvas::internalSave(SaveFlags flags) {
740 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000741
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 MCRec* newTop = (MCRec*)fMCStack.push_back();
743 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000744
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 newTop->fNext = fMCRec;
746 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000747
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000748 if (SkCanvas::kClip_SaveFlag & flags) {
749 fClipStack.save();
750 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000751
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 return saveCount;
753}
754
755int SkCanvas::save(SaveFlags flags) {
756 // call shared impl
757 return this->internalSave(flags);
758}
759
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
761 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
762}
763
junov@chromium.orga907ac32012-02-24 21:54:07 +0000764bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000765 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000766 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000767 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000768 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000769 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000770 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000771
772 if (imageFilter) {
773 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
774 // Filters may grow the bounds beyond the device bounds.
775 op = SkRegion::kReplace_Op;
776 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000777 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 if (NULL != bounds) {
779 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000780
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 this->getTotalMatrix().mapRect(&r, *bounds);
782 r.roundOut(&ir);
783 // early exit if the layer's bounds are clipped out
784 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000785 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000786 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000787 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000788 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 }
790 } else { // no user bounds, so just use the clip
791 ir = clipBounds;
792 }
793
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000794 if (bounds_affects_clip(flags)) {
795 fClipStack.clipDevRect(ir, op);
796 // early exit if the clip is now empty
797 if (!fMCRec->fRasterClip->op(ir, op)) {
798 return false;
799 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000800 }
801
802 if (intersection) {
803 *intersection = ir;
804 }
805 return true;
806}
807
808int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
809 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000810 return this->internalSaveLayer(bounds, paint, flags, false);
811}
812
reed@google.com76f10a32014-02-05 15:32:21 +0000813static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000814 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000815 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000816 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000817}
818
reed@google.com8926b162012-03-23 15:36:36 +0000819int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
820 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000821 // do this before we create the layer. We don't call the public save() since
822 // that would invoke a possibly overridden virtual
823 int count = this->internalSave(flags);
824
825 fDeviceCMDirty = true;
826
827 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000828 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 return count;
830 }
831
reed@google.comb55deeb2012-01-06 14:43:09 +0000832 // Kill the imagefilter if our device doesn't allow it
833 SkLazyPaint lazyP;
834 if (paint && paint->getImageFilter()) {
835 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000836 if (justForImageFilter) {
837 // early exit if the layer was just for the imageFilter
838 return count;
839 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000840 SkPaint* p = lazyP.set(*paint);
841 p->setImageFilter(NULL);
842 paint = p;
843 }
844 }
845
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000846 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
847 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
848 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000850 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000851 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000852 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000853 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000854 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000855 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000856 if (NULL == device) {
857 SkDebugf("Unable to create device for layer.");
858 return count;
859 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000860
reed@google.com6f8f2922011-03-04 22:27:10 +0000861 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000862 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 device->unref();
864
865 layer->fNext = fMCRec->fTopLayer;
866 fMCRec->fLayer = layer;
867 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
868
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000869 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 return count;
871}
872
873int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
874 SaveFlags flags) {
875 if (0xFF == alpha) {
876 return this->saveLayer(bounds, NULL, flags);
877 } else {
878 SkPaint tmpPaint;
879 tmpPaint.setAlpha(alpha);
880 return this->saveLayer(bounds, &tmpPaint, flags);
881 }
882}
883
884void SkCanvas::restore() {
885 // check for underflow
886 if (fMCStack.count() > 1) {
887 this->internalRestore();
888 }
889}
890
891void SkCanvas::internalRestore() {
892 SkASSERT(fMCStack.count() != 0);
893
894 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000895 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000897 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
898 fClipStack.restore();
899 }
900
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000901 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 DeviceCM* layer = fMCRec->fLayer; // may be null
903 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
904 fMCRec->fLayer = NULL;
905
906 // now do the normal restore()
907 fMCRec->~MCRec(); // balanced in save()
908 fMCStack.pop_back();
909 fMCRec = (MCRec*)fMCStack.back();
910
911 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
912 since if we're being recorded, we don't want to record this (the
913 recorder will have already recorded the restore).
914 */
915 if (NULL != layer) {
916 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000917 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000918 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
919 layer->fPaint);
920 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000922
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000923 SkASSERT(fSaveLayerCount > 0);
924 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 }
926 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000927 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928}
929
930int SkCanvas::getSaveCount() const {
931 return fMCStack.count();
932}
933
934void SkCanvas::restoreToCount(int count) {
935 // sanity check
936 if (count < 1) {
937 count = 1;
938 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000939
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000940 int n = this->getSaveCount() - count;
941 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 this->restore();
943 }
944}
945
reed@google.com7c202932011-12-14 18:48:05 +0000946bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000947 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000948}
949
reed@google.com76f10a32014-02-05 15:32:21 +0000950SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
951 return this->onNewSurface(info);
952}
953
954SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
955 SkBaseDevice* dev = this->getDevice();
956 return dev ? dev->newSurface(info) : NULL;
957}
958
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000959SkImageInfo SkCanvas::imageInfo() const {
960 SkBaseDevice* dev = this->getDevice();
961 if (dev) {
962 return dev->imageInfo();
963 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000964 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000965 }
966}
967
968const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
969 return this->onPeekPixels(info, rowBytes);
970}
971
972const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
973 SkBaseDevice* dev = this->getDevice();
974 return dev ? dev->peekPixels(info, rowBytes) : NULL;
975}
976
977SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
978 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
979 if (NULL == fAddr) {
980 fInfo = canvas->imageInfo();
981 if (kUnknown_SkColorType == fInfo.colorType() ||
982 !fBitmap.allocPixels(fInfo))
983 {
984 return; // failure, fAddr is NULL
985 }
986 fBitmap.lockPixels();
987 if (!canvas->readPixels(&fBitmap, 0, 0)) {
988 return; // failure, fAddr is NULL
989 }
990 fAddr = fBitmap.getPixels();
991 fRowBytes = fBitmap.rowBytes();
992 }
993 SkASSERT(fAddr); // success
994}
995
996bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
997 if (fAddr) {
998 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
999 NULL, NULL);
1000 } else {
1001 bitmap->reset();
1002 return false;
1003 }
1004}
1005
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001006void SkCanvas::onPushCull(const SkRect& cullRect) {
1007 // do nothing. Subclasses may do something
1008}
1009
1010void SkCanvas::onPopCull() {
1011 // do nothing. Subclasses may do something
1012}
1013
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014/////////////////////////////////////////////////////////////////////////////
1015
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001016void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001018 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 return;
1020 }
1021
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001022 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001024 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001026
1027 SkDEBUGCODE(bitmap.validate();)
1028 CHECK_LOCKCOUNT_BALANCE(bitmap);
1029
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001030 SkRect storage;
1031 const SkRect* bounds = NULL;
1032 if (paint && paint->canComputeFastBounds()) {
1033 bitmap.getBounds(&storage);
1034 matrix.mapRect(&storage);
1035 bounds = &paint->computeFastBounds(storage, &storage);
1036 }
1037
1038 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001039
1040 while (iter.next()) {
1041 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1042 }
1043
1044 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045}
1046
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001047void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001048 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 SkPaint tmp;
1050 if (NULL == paint) {
1051 tmp.setDither(true);
1052 paint = &tmp;
1053 }
reed@google.com4b226022011-01-11 18:32:13 +00001054
reed@google.com8926b162012-03-23 15:36:36 +00001055 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001057 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001058 paint = &looper.paint();
1059 SkImageFilter* filter = paint->getImageFilter();
1060 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001061 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001062 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001063 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001064 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001065 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001066 SkMatrix matrix = *iter.fMatrix;
1067 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001068 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001069 SkPaint tmpUnfiltered(*paint);
1070 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001071 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1072 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001073 }
1074 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001075 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001076 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001078 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079}
1080
reed@google.com8926b162012-03-23 15:36:36 +00001081void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1082 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001083 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001084 return;
1085 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001086 SkDEBUGCODE(bitmap.validate();)
1087 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001088
reed@google.com8926b162012-03-23 15:36:36 +00001089 SkPaint tmp;
1090 if (NULL == paint) {
1091 paint = &tmp;
1092 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001093
reed@google.com8926b162012-03-23 15:36:36 +00001094 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001095
reed@google.com8926b162012-03-23 15:36:36 +00001096 while (iter.next()) {
1097 paint = &looper.paint();
1098 SkImageFilter* filter = paint->getImageFilter();
1099 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1100 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001101 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001102 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001103 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001104 SkMatrix matrix = *iter.fMatrix;
1105 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001106 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001107 SkPaint tmpUnfiltered(*paint);
1108 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001109 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001110 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001111 }
1112 } else {
1113 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1114 }
1115 }
1116 LOOPER_END
1117}
1118
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119/////////////////////////////////////////////////////////////////////////////
1120
1121bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1122 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001123 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 return fMCRec->fMatrix->preTranslate(dx, dy);
1125}
1126
1127bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1128 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001129 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 return fMCRec->fMatrix->preScale(sx, sy);
1131}
1132
1133bool SkCanvas::rotate(SkScalar degrees) {
1134 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001135 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 return fMCRec->fMatrix->preRotate(degrees);
1137}
1138
1139bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1140 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001141 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 return fMCRec->fMatrix->preSkew(sx, sy);
1143}
1144
1145bool SkCanvas::concat(const SkMatrix& matrix) {
1146 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001147 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 return fMCRec->fMatrix->preConcat(matrix);
1149}
1150
1151void SkCanvas::setMatrix(const SkMatrix& matrix) {
1152 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001153 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 *fMCRec->fMatrix = matrix;
1155}
1156
1157// this is not virtual, so it must call a virtual method so that subclasses
1158// will see its action
1159void SkCanvas::resetMatrix() {
1160 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001161
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 matrix.reset();
1163 this->setMatrix(matrix);
1164}
1165
1166//////////////////////////////////////////////////////////////////////////////
1167
reed@google.comc42d35d2011-10-12 11:57:42 +00001168bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001169 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1170 this->onClipRect(rect, op, edgeStyle);
1171 return !this->isClipEmpty();
1172}
1173
1174void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001175#ifdef SK_ENABLE_CLIP_QUICKREJECT
1176 if (SkRegion::kIntersect_Op == op) {
1177 if (fMCRec->fRasterClip->isEmpty()) {
1178 return false;
1179 }
1180
reed@google.com3b3e8952012-08-16 20:53:31 +00001181 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001182 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001183 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001184
1185 fClipStack.clipEmpty();
1186 return fMCRec->fRasterClip->setEmpty();
1187 }
1188 }
1189#endif
1190
reed@google.com5c3d1472011-02-22 19:12:23 +00001191 AutoValidateClip avc(this);
1192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001194 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001195 if (!fAllowSoftClip) {
1196 edgeStyle = kHard_ClipEdgeStyle;
1197 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198
1199 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001200 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001201 // the matrix. This means we don't have to a) make a path, and b) tell
1202 // the region code to scan-convert the path, only to discover that it
1203 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205
1206 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001207 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1208 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001210 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001211 // and clip against that, since it can handle any matrix. However, to
1212 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1213 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 SkPath path;
1215
1216 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001217 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218 }
1219}
1220
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001221static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1222 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001223 // base is used to limit the size (and therefore memory allocation) of the
1224 // region that results from scan converting devPath.
1225 SkRegion base;
1226
reed@google.com819c9212011-02-23 18:56:55 +00001227 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001228 // since we are intersect, we can do better (tighter) with currRgn's
1229 // bounds, than just using the device. However, if currRgn is complex,
1230 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001231 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001232 // FIXME: we should also be able to do this when currClip->isBW(),
1233 // but relaxing the test above triggers GM asserts in
1234 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001235 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001236 } else {
reed@google.com00177082011-10-12 14:34:30 +00001237 base.setRect(currClip->getBounds());
1238 SkRasterClip clip;
1239 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001240 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001241 }
reed@google.com819c9212011-02-23 18:56:55 +00001242 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001243 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001244 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001245 currClip->setEmpty();
1246 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001247 }
1248
junov@chromium.orga907ac32012-02-24 21:54:07 +00001249 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001250
1251 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001252 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001253 } else {
reed@google.com00177082011-10-12 14:34:30 +00001254 SkRasterClip clip;
1255 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001256 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001257 }
1258 }
1259}
1260
reed@google.com4ed0fb72012-12-12 20:48:18 +00001261bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001262 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001263 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001264 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1265 } else {
1266 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001267 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001268 return !this->isClipEmpty();
1269}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001270
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001271void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001272 SkRRect transformedRRect;
1273 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1274 AutoValidateClip avc(this);
1275
1276 fDeviceCMDirty = true;
1277 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001278 if (!fAllowSoftClip) {
1279 edgeStyle = kHard_ClipEdgeStyle;
1280 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001281
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001282 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001283
1284 SkPath devPath;
1285 devPath.addRRect(transformedRRect);
1286
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001287 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1288 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001289 }
1290
1291 SkPath path;
1292 path.addRRect(rrect);
1293 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001294 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001295}
1296
reed@google.comc42d35d2011-10-12 11:57:42 +00001297bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001298 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1299 SkRect r;
1300 if (!path.isInverseFillType() && path.isRect(&r)) {
1301 this->onClipRect(r, op, edgeStyle);
1302 } else {
1303 this->onClipPath(path, op, edgeStyle);
1304 }
1305
1306 return !this->isClipEmpty();
1307}
1308
1309void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001310#ifdef SK_ENABLE_CLIP_QUICKREJECT
1311 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1312 if (fMCRec->fRasterClip->isEmpty()) {
1313 return false;
1314 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001315
reed@google.com3b3e8952012-08-16 20:53:31 +00001316 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001317 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001318 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001319
reed@google.comda17f752012-08-16 18:27:05 +00001320 fClipStack.clipEmpty();
1321 return fMCRec->fRasterClip->setEmpty();
1322 }
1323 }
1324#endif
1325
reed@google.com5c3d1472011-02-22 19:12:23 +00001326 AutoValidateClip avc(this);
1327
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001329 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001330 if (!fAllowSoftClip) {
1331 edgeStyle = kHard_ClipEdgeStyle;
1332 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333
1334 SkPath devPath;
1335 path.transform(*fMCRec->fMatrix, &devPath);
1336
reed@google.comfe701122011-11-08 19:41:23 +00001337 // Check if the transfomation, or the original path itself
1338 // made us empty. Note this can also happen if we contained NaN
1339 // values. computing the bounds detects this, and will set our
1340 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1341 if (devPath.getBounds().isEmpty()) {
1342 // resetting the path will remove any NaN or other wanky values
1343 // that might upset our scan converter.
1344 devPath.reset();
1345 }
1346
reed@google.com5c3d1472011-02-22 19:12:23 +00001347 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001348 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001349
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001350 if (fAllowSimplifyClip) {
1351 devPath.reset();
1352 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1353 const SkClipStack* clipStack = getClipStack();
1354 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1355 const SkClipStack::Element* element;
1356 while ((element = iter.next())) {
1357 SkClipStack::Element::Type type = element->getType();
1358 if (type == SkClipStack::Element::kEmpty_Type) {
1359 continue;
1360 }
1361 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001362 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001363 SkRegion::Op elementOp = element->getOp();
1364 if (elementOp == SkRegion::kReplace_Op) {
1365 devPath = operand;
1366 } else {
1367 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1368 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001369 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1370 // perhaps we need an API change to avoid this sort of mixed-signals about
1371 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001372 if (element->isAA()) {
1373 edgeStyle = kSoft_ClipEdgeStyle;
1374 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001375 }
1376 op = SkRegion::kReplace_Op;
1377 }
1378
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001379 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380}
1381
robertphillips@google.com03fc3b42014-02-28 15:28:02 +00001382bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001383 bool inverseFilled) {
1384 // This is for updating the clip conservatively using only bounds
1385 // information.
1386 // Contract:
1387 // The current clip must contain the true clip. The true
1388 // clip is the clip that would have normally been computed
1389 // by calls to clipPath and clipRRect
1390 // Objective:
1391 // Keep the current clip as small as possible without
1392 // breaking the contract, using only clip bounding rectangles
1393 // (for performance).
1394
1395 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1396 // don't have to worry about getting caught in a loop. Thus anywhere
1397 // we call a virtual method, we explicitly prefix it with
1398 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001399
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001400 if (inverseFilled) {
1401 switch (op) {
1402 case SkRegion::kIntersect_Op:
1403 case SkRegion::kDifference_Op:
1404 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001405 // the clip unchanged conservatively respects the contract.
1406 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001407 case SkRegion::kUnion_Op:
1408 case SkRegion::kReplace_Op:
1409 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001410 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001411 // These ops can grow the current clip up to the extents of
1412 // the input clip, which is inverse filled, so we just set
1413 // the current clip to the device bounds.
1414 SkRect deviceBounds;
1415 SkIRect deviceIBounds;
1416 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001417 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001418 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1419 // set the clip in device space
1420 this->SkCanvas::setMatrix(SkMatrix::I());
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
1422 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001423 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001424 break;
1425 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001426 default:
1427 SkASSERT(0); // unhandled op?
1428 }
1429 } else {
1430 // Not inverse filled
1431 switch (op) {
1432 case SkRegion::kIntersect_Op:
1433 case SkRegion::kUnion_Op:
1434 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001435 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1436 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001437 case SkRegion::kDifference_Op:
1438 // Difference can only shrink the current clip.
1439 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001440 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001441 case SkRegion::kReverseDifference_Op:
1442 // To reverse, we swap in the bounds with a replace op.
1443 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001444 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1445 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001446 case SkRegion::kXOR_Op:
1447 // Be conservative, based on (A XOR B) always included in (A union B),
1448 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001449 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1450 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001451 default:
1452 SkASSERT(0); // unhandled op?
1453 }
1454 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001455
1456 return !this->isClipEmpty();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001457}
1458
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001460 this->onClipRegion(rgn, op);
1461 return !this->isClipEmpty();
1462}
1463
1464void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001465 AutoValidateClip avc(this);
1466
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001468 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469
reed@google.com5c3d1472011-02-22 19:12:23 +00001470 // todo: signal fClipStack that we have a region, and therefore (I guess)
1471 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001472 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001473
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001474 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475}
1476
reed@google.com819c9212011-02-23 18:56:55 +00001477#ifdef SK_DEBUG
1478void SkCanvas::validateClip() const {
1479 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001480 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001481 if (!device) {
1482 SkASSERT(this->getTotalClip().isEmpty());
1483 return;
1484 }
1485
reed@google.com819c9212011-02-23 18:56:55 +00001486 SkIRect ir;
1487 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001488 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001489
robertphillips@google.com80214e22012-07-20 15:33:18 +00001490 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001491 const SkClipStack::Element* element;
1492 while ((element = iter.next()) != NULL) {
1493 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001494 case SkClipStack::Element::kRect_Type:
1495 element->getRect().round(&ir);
1496 tmpClip.op(ir, element->getOp());
1497 break;
1498 case SkClipStack::Element::kEmpty_Type:
1499 tmpClip.setEmpty();
1500 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001501 default: {
1502 SkPath path;
1503 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001504 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001505 break;
1506 }
reed@google.com819c9212011-02-23 18:56:55 +00001507 }
1508 }
1509
reed@google.com6f8f2922011-03-04 22:27:10 +00001510#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001511 // now compare against the current rgn
1512 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001513 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001514#endif
reed@google.com819c9212011-02-23 18:56:55 +00001515}
1516#endif
1517
reed@google.com90c07ea2012-04-13 13:50:27 +00001518void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001519 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001520 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001521
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001522 static const SkRect kEmpty = { 0, 0, 0, 0 };
1523 while ((element = iter.next()) != NULL) {
1524 switch (element->getType()) {
1525 case SkClipStack::Element::kPath_Type:
1526 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1527 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001528 case SkClipStack::Element::kRRect_Type:
1529 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1530 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001531 case SkClipStack::Element::kRect_Type:
1532 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1533 break;
1534 case SkClipStack::Element::kEmpty_Type:
1535 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1536 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001537 }
1538 }
1539}
1540
reed@google.com5c3d1472011-02-22 19:12:23 +00001541///////////////////////////////////////////////////////////////////////////////
1542
reed@google.com754de5f2014-02-24 19:38:20 +00001543bool SkCanvas::isClipEmpty() const {
1544 return fMCRec->fRasterClip->isEmpty();
1545}
1546
reed@google.com3b3e8952012-08-16 20:53:31 +00001547bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001548
reed@google.com16078632011-12-06 18:56:37 +00001549 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001550 return true;
1551
reed@google.com00177082011-10-12 14:34:30 +00001552 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553 return true;
1554 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555
tomhudson@google.com8d430182011-06-06 19:11:19 +00001556 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001557 SkRect dst;
1558 fMCRec->fMatrix->mapRect(&dst, rect);
1559 SkIRect idst;
1560 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001561 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001562 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001563 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001564
reed@android.coma380ae42009-07-21 01:17:02 +00001565 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001566 // TODO: should we use | instead, or compare all 4 at once?
1567 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001568 return true;
1569 }
reed@google.comc0784db2013-12-13 21:16:12 +00001570 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001571 return true;
1572 }
1573 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
reed@google.com3b3e8952012-08-16 20:53:31 +00001577bool SkCanvas::quickReject(const SkPath& path) const {
1578 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579}
1580
reed@google.com3b3e8952012-08-16 20:53:31 +00001581bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001582 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 return false;
1585 }
1586
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001587 SkMatrix inverse;
1588 // if we can't invert the CTM, we can't return local clip bounds
1589 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001590 if (bounds) {
1591 bounds->setEmpty();
1592 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001593 return false;
1594 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001596 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001597 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001598 // adjust it outwards in case we are antialiasing
1599 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001600
reed@google.com8f4d2302013-12-17 16:44:46 +00001601 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1602 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 inverse.mapRect(bounds, r);
1604 }
1605 return true;
1606}
1607
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001608bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001609 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001610 if (clip.isEmpty()) {
1611 if (bounds) {
1612 bounds->setEmpty();
1613 }
1614 return false;
1615 }
1616
1617 if (NULL != bounds) {
1618 *bounds = clip.getBounds();
1619 }
1620 return true;
1621}
1622
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623const SkMatrix& SkCanvas::getTotalMatrix() const {
1624 return *fMCRec->fMatrix;
1625}
1626
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001627SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001628 if (fMCRec->fRasterClip->isEmpty()) {
1629 return kEmpty_ClipType;
1630 }
1631 if (fMCRec->fRasterClip->isRect()) {
1632 return kRect_ClipType;
1633 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001634 return kComplex_ClipType;
1635}
1636
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001638 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639}
1640
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001641SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001642 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001643 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644}
1645
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001646GrContext* SkCanvas::getGrContext() {
1647#if SK_SUPPORT_GPU
1648 SkBaseDevice* device = this->getTopDevice();
1649 if (NULL != device) {
1650 GrRenderTarget* renderTarget = device->accessRenderTarget();
1651 if (NULL != renderTarget) {
1652 return renderTarget->getContext();
1653 }
1654 }
1655#endif
1656
1657 return NULL;
1658
1659}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001660
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001661void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1662 const SkPaint& paint) {
1663 if (outer.isEmpty()) {
1664 return;
1665 }
1666 if (inner.isEmpty()) {
1667 this->drawRRect(outer, paint);
1668 return;
1669 }
1670
1671 // We don't have this method (yet), but technically this is what we should
1672 // be able to assert...
1673 // SkASSERT(outer.contains(inner));
1674 //
1675 // For now at least check for containment of bounds
1676 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1677
1678 this->onDrawDRRect(outer, inner, paint);
1679}
1680
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681//////////////////////////////////////////////////////////////////////////////
1682// These are the virtual drawing methods
1683//////////////////////////////////////////////////////////////////////////////
1684
reed@google.com2a981812011-04-14 18:59:28 +00001685void SkCanvas::clear(SkColor color) {
1686 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001687 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001688 while (iter.next()) {
1689 iter.fDevice->clear(color);
1690 }
1691}
1692
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001694 this->internalDrawPaint(paint);
1695}
1696
1697void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001698 CHECK_SHADER_NOSETCONTEXT(paint);
1699
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001700 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701
1702 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001703 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 }
1705
reed@google.com4e2b3d32011-04-07 14:18:59 +00001706 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707}
1708
1709void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1710 const SkPaint& paint) {
1711 if ((long)count <= 0) {
1712 return;
1713 }
1714
reed@google.comea033602012-12-14 13:13:55 +00001715 CHECK_SHADER_NOSETCONTEXT(paint);
1716
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001717 SkRect r, storage;
1718 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001719 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001720 // special-case 2 points (common for drawing a single line)
1721 if (2 == count) {
1722 r.set(pts[0], pts[1]);
1723 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001724 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001725 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001726 bounds = &paint.computeFastStrokeBounds(r, &storage);
1727 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001728 return;
1729 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001730 }
reed@google.coma584aed2012-05-16 14:06:02 +00001731
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 SkASSERT(pts != NULL);
1733
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001734 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001735
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001737 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738 }
reed@google.com4b226022011-01-11 18:32:13 +00001739
reed@google.com4e2b3d32011-04-07 14:18:59 +00001740 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741}
1742
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001743void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001744 CHECK_SHADER_NOSETCONTEXT(paint);
1745
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001746 SkRect storage;
1747 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001749 bounds = &paint.computeFastBounds(r, &storage);
1750 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 return;
1752 }
1753 }
reed@google.com4b226022011-01-11 18:32:13 +00001754
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001755 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756
1757 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001758 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759 }
1760
reed@google.com4e2b3d32011-04-07 14:18:59 +00001761 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762}
1763
reed@google.com4ed0fb72012-12-12 20:48:18 +00001764void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001765 CHECK_SHADER_NOSETCONTEXT(paint);
1766
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001767 SkRect storage;
1768 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001769 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001770 bounds = &paint.computeFastBounds(oval, &storage);
1771 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001772 return;
1773 }
1774 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001775
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001776 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001777
1778 while (iter.next()) {
1779 iter.fDevice->drawOval(iter, oval, looper.paint());
1780 }
1781
1782 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001783}
1784
1785void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001786 CHECK_SHADER_NOSETCONTEXT(paint);
1787
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001788 SkRect storage;
1789 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001790 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001791 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1792 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001793 return;
1794 }
1795 }
1796
1797 if (rrect.isRect()) {
1798 // call the non-virtual version
1799 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001800 return;
1801 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001802 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001803 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1804 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001805 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001806
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001807 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001808
1809 while (iter.next()) {
1810 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1811 }
1812
1813 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001814}
1815
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001816void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1817 const SkPaint& paint) {
1818 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001819
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001820 SkRect storage;
1821 const SkRect* bounds = NULL;
1822 if (paint.canComputeFastBounds()) {
1823 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1824 if (this->quickReject(*bounds)) {
1825 return;
1826 }
1827 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001828
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001829 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001830
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001831 while (iter.next()) {
1832 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1833 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001834
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001835 LOOPER_END
1836}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001837
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001838void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001839 CHECK_SHADER_NOSETCONTEXT(paint);
1840
reed@google.com93645112012-07-26 16:11:47 +00001841 if (!path.isFinite()) {
1842 return;
1843 }
1844
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001845 SkRect storage;
1846 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001847 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001848 const SkRect& pathBounds = path.getBounds();
1849 bounds = &paint.computeFastBounds(pathBounds, &storage);
1850 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 return;
1852 }
1853 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001854
1855 const SkRect& r = path.getBounds();
1856 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001857 if (path.isInverseFillType()) {
1858 this->internalDrawPaint(paint);
1859 }
1860 return;
1861 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001863 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864
1865 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001866 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 }
1868
reed@google.com4e2b3d32011-04-07 14:18:59 +00001869 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870}
1871
1872void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1873 const SkPaint* paint) {
1874 SkDEBUGCODE(bitmap.validate();)
1875
reed@google.com3d608122011-11-21 15:16:16 +00001876 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001877 SkRect bounds = {
1878 x, y,
1879 x + SkIntToScalar(bitmap.width()),
1880 y + SkIntToScalar(bitmap.height())
1881 };
1882 if (paint) {
1883 (void)paint->computeFastBounds(bounds, &bounds);
1884 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001885 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886 return;
1887 }
1888 }
reed@google.com4b226022011-01-11 18:32:13 +00001889
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 SkMatrix matrix;
1891 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001892 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893}
1894
reed@google.com9987ec32011-09-07 11:57:52 +00001895// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001896void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001897 const SkRect& dst, const SkPaint* paint,
1898 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001899 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900 return;
1901 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001902
reed@google.comea033602012-12-14 13:13:55 +00001903 CHECK_LOCKCOUNT_BALANCE(bitmap);
1904
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001905 SkRect storage;
1906 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001907 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001908 if (paint) {
1909 bounds = &paint->computeFastBounds(dst, &storage);
1910 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001911 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001912 return;
1913 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914 }
reed@google.com3d608122011-11-21 15:16:16 +00001915
reed@google.com33535f32012-09-25 15:37:50 +00001916 SkLazyPaint lazy;
1917 if (NULL == paint) {
1918 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001920
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001921 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001922
reed@google.com33535f32012-09-25 15:37:50 +00001923 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001924 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001925 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001926
reed@google.com33535f32012-09-25 15:37:50 +00001927 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928}
1929
reed@google.com71121732012-09-18 15:14:33 +00001930void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001931 const SkRect& dst, const SkPaint* paint,
1932 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001933 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001934 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001935}
1936
reed@android.com8a1c16f2008-12-17 15:59:43 +00001937void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1938 const SkPaint* paint) {
1939 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001940 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941}
1942
reed@google.com9987ec32011-09-07 11:57:52 +00001943void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1944 const SkIRect& center, const SkRect& dst,
1945 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001946 if (bitmap.drawsNothing()) {
1947 return;
1948 }
reed@google.com3d608122011-11-21 15:16:16 +00001949 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001950 SkRect storage;
1951 const SkRect* bounds = &dst;
1952 if (paint) {
1953 bounds = &paint->computeFastBounds(dst, &storage);
1954 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001955 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001956 return;
1957 }
1958 }
1959
reed@google.com9987ec32011-09-07 11:57:52 +00001960 const int32_t w = bitmap.width();
1961 const int32_t h = bitmap.height();
1962
1963 SkIRect c = center;
1964 // pin center to the bounds of the bitmap
1965 c.fLeft = SkMax32(0, center.fLeft);
1966 c.fTop = SkMax32(0, center.fTop);
1967 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1968 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1969
reed@google.com71121732012-09-18 15:14:33 +00001970 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001971 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001972 };
1973 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001974 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001975 };
reed@google.com9987ec32011-09-07 11:57:52 +00001976 SkScalar dstX[4] = {
1977 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1978 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1979 };
1980 SkScalar dstY[4] = {
1981 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1982 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1983 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001984
reed@google.com9987ec32011-09-07 11:57:52 +00001985 if (dstX[1] > dstX[2]) {
1986 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1987 dstX[2] = dstX[1];
1988 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001989
reed@google.com9987ec32011-09-07 11:57:52 +00001990 if (dstY[1] > dstY[2]) {
1991 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1992 dstY[2] = dstY[1];
1993 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001994
reed@google.com9987ec32011-09-07 11:57:52 +00001995 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001996 SkRect s, d;
1997
reed@google.com9987ec32011-09-07 11:57:52 +00001998 s.fTop = srcY[y];
1999 s.fBottom = srcY[y+1];
2000 d.fTop = dstY[y];
2001 d.fBottom = dstY[y+1];
2002 for (int x = 0; x < 3; x++) {
2003 s.fLeft = srcX[x];
2004 s.fRight = srcX[x+1];
2005 d.fLeft = dstX[x];
2006 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002007 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002008 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002009 }
2010 }
2011}
2012
2013void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2014 const SkRect& dst, const SkPaint* paint) {
2015 SkDEBUGCODE(bitmap.validate();)
2016
2017 // Need a device entry-point, so gpu can use a mesh
2018 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2019}
2020
reed@google.comf67e4cf2011-03-15 20:56:58 +00002021class SkDeviceFilteredPaint {
2022public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002023 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2024 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002025 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002026 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002027 newPaint->setFlags(flags.fFlags);
2028 newPaint->setHinting(flags.fHinting);
2029 fPaint = newPaint;
2030 } else {
2031 fPaint = &paint;
2032 }
2033 }
2034
reed@google.comf67e4cf2011-03-15 20:56:58 +00002035 const SkPaint& paint() const { return *fPaint; }
2036
2037private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002038 const SkPaint* fPaint;
2039 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002040};
2041
bungeman@google.com52c748b2011-08-22 21:30:43 +00002042void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2043 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002044 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002045 draw.fDevice->drawRect(draw, r, paint);
2046 } else {
2047 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002048 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002049 draw.fDevice->drawRect(draw, r, p);
2050 }
2051}
2052
2053void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2054 const char text[], size_t byteLength,
2055 SkScalar x, SkScalar y) {
2056 SkASSERT(byteLength == 0 || text != NULL);
2057
2058 // nothing to draw
2059 if (text == NULL || byteLength == 0 ||
2060 draw.fClip->isEmpty() ||
2061 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2062 return;
2063 }
2064
2065 SkScalar width = 0;
2066 SkPoint start;
2067
2068 start.set(0, 0); // to avoid warning
2069 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2070 SkPaint::kStrikeThruText_Flag)) {
2071 width = paint.measureText(text, byteLength);
2072
2073 SkScalar offsetX = 0;
2074 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2075 offsetX = SkScalarHalf(width);
2076 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2077 offsetX = width;
2078 }
2079 start.set(x - offsetX, y);
2080 }
2081
2082 if (0 == width) {
2083 return;
2084 }
2085
2086 uint32_t flags = paint.getFlags();
2087
2088 if (flags & (SkPaint::kUnderlineText_Flag |
2089 SkPaint::kStrikeThruText_Flag)) {
2090 SkScalar textSize = paint.getTextSize();
2091 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2092 SkRect r;
2093
2094 r.fLeft = start.fX;
2095 r.fRight = start.fX + width;
2096
2097 if (flags & SkPaint::kUnderlineText_Flag) {
2098 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2099 start.fY);
2100 r.fTop = offset;
2101 r.fBottom = offset + height;
2102 DrawRect(draw, paint, r, textSize);
2103 }
2104 if (flags & SkPaint::kStrikeThruText_Flag) {
2105 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2106 start.fY);
2107 r.fTop = offset;
2108 r.fBottom = offset + height;
2109 DrawRect(draw, paint, r, textSize);
2110 }
2111 }
2112}
2113
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114void SkCanvas::drawText(const void* text, size_t byteLength,
2115 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002116 CHECK_SHADER_NOSETCONTEXT(paint);
2117
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002118 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119
2120 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002121 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002122 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002123 DrawTextDecorations(iter, dfp.paint(),
2124 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125 }
2126
reed@google.com4e2b3d32011-04-07 14:18:59 +00002127 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128}
2129
2130void SkCanvas::drawPosText(const void* text, size_t byteLength,
2131 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002132 CHECK_SHADER_NOSETCONTEXT(paint);
2133
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002134 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002135
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002137 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002138 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002139 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140 }
reed@google.com4b226022011-01-11 18:32:13 +00002141
reed@google.com4e2b3d32011-04-07 14:18:59 +00002142 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143}
2144
2145void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2146 const SkScalar xpos[], SkScalar constY,
2147 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002148 CHECK_SHADER_NOSETCONTEXT(paint);
2149
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002150 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002151
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002153 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002154 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002155 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156 }
reed@google.com4b226022011-01-11 18:32:13 +00002157
reed@google.com4e2b3d32011-04-07 14:18:59 +00002158 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159}
2160
2161void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2162 const SkPath& path, const SkMatrix* matrix,
2163 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002164 CHECK_SHADER_NOSETCONTEXT(paint);
2165
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002166 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167
2168 while (iter.next()) {
2169 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002170 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171 }
2172
reed@google.com4e2b3d32011-04-07 14:18:59 +00002173 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174}
2175
2176void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2177 const SkPoint verts[], const SkPoint texs[],
2178 const SkColor colors[], SkXfermode* xmode,
2179 const uint16_t indices[], int indexCount,
2180 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002181 CHECK_SHADER_NOSETCONTEXT(paint);
2182
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002183 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002184
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185 while (iter.next()) {
2186 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002187 colors, xmode, indices, indexCount,
2188 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 }
reed@google.com4b226022011-01-11 18:32:13 +00002190
reed@google.com4e2b3d32011-04-07 14:18:59 +00002191 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192}
2193
2194//////////////////////////////////////////////////////////////////////////////
2195// These methods are NOT virtual, and therefore must call back into virtual
2196// methods, rather than actually drawing themselves.
2197//////////////////////////////////////////////////////////////////////////////
2198
2199void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002200 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 SkPaint paint;
2202
2203 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002204 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002205 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 }
2207 this->drawPaint(paint);
2208}
2209
reed@android.com845fdac2009-06-23 03:01:32 +00002210void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 SkPaint paint;
2212
2213 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002214 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002215 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216 }
2217 this->drawPaint(paint);
2218}
2219
2220void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2221 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002222
reed@android.com8a1c16f2008-12-17 15:59:43 +00002223 pt.set(x, y);
2224 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2225}
2226
2227void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2228 SkPoint pt;
2229 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002230
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231 pt.set(x, y);
2232 paint.setColor(color);
2233 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2234}
2235
2236void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2237 const SkPaint& paint) {
2238 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002239
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240 pts[0].set(x0, y0);
2241 pts[1].set(x1, y1);
2242 this->drawPoints(kLines_PointMode, 2, pts, paint);
2243}
2244
2245void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2246 SkScalar right, SkScalar bottom,
2247 const SkPaint& paint) {
2248 SkRect r;
2249
2250 r.set(left, top, right, bottom);
2251 this->drawRect(r, paint);
2252}
2253
2254void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2255 const SkPaint& paint) {
2256 if (radius < 0) {
2257 radius = 0;
2258 }
2259
2260 SkRect r;
2261 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002262 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263}
2264
2265void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2266 const SkPaint& paint) {
2267 if (rx > 0 && ry > 0) {
2268 if (paint.canComputeFastBounds()) {
2269 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002270 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271 return;
2272 }
2273 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002274 SkRRect rrect;
2275 rrect.setRectXY(r, rx, ry);
2276 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 } else {
2278 this->drawRect(r, paint);
2279 }
2280}
2281
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2283 SkScalar sweepAngle, bool useCenter,
2284 const SkPaint& paint) {
2285 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2286 this->drawOval(oval, paint);
2287 } else {
2288 SkPath path;
2289 if (useCenter) {
2290 path.moveTo(oval.centerX(), oval.centerY());
2291 }
2292 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2293 if (useCenter) {
2294 path.close();
2295 }
2296 this->drawPath(path, paint);
2297 }
2298}
2299
2300void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2301 const SkPath& path, SkScalar hOffset,
2302 SkScalar vOffset, const SkPaint& paint) {
2303 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002304
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 matrix.setTranslate(hOffset, vOffset);
2306 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2307}
2308
reed@android.comf76bacf2009-05-13 14:00:33 +00002309///////////////////////////////////////////////////////////////////////////////
2310
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313}
2314
reed@android.com8a1c16f2008-12-17 15:59:43 +00002315///////////////////////////////////////////////////////////////////////////////
2316///////////////////////////////////////////////////////////////////////////////
2317
2318SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002319 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320
2321 SkASSERT(canvas);
2322
2323 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2324 fDone = !fImpl->next();
2325}
2326
2327SkCanvas::LayerIter::~LayerIter() {
2328 fImpl->~SkDrawIter();
2329}
2330
2331void SkCanvas::LayerIter::next() {
2332 fDone = !fImpl->next();
2333}
2334
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002335SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 return fImpl->getDevice();
2337}
2338
2339const SkMatrix& SkCanvas::LayerIter::matrix() const {
2340 return fImpl->getMatrix();
2341}
2342
2343const SkPaint& SkCanvas::LayerIter::paint() const {
2344 const SkPaint* paint = fImpl->getPaint();
2345 if (NULL == paint) {
2346 paint = &fDefaultPaint;
2347 }
2348 return *paint;
2349}
2350
2351const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2352int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2353int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002354
2355///////////////////////////////////////////////////////////////////////////////
2356
2357SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002358
2359///////////////////////////////////////////////////////////////////////////////
2360
2361static bool supported_for_raster_canvas(const SkImageInfo& info) {
2362 switch (info.alphaType()) {
2363 case kPremul_SkAlphaType:
2364 case kOpaque_SkAlphaType:
2365 break;
2366 default:
2367 return false;
2368 }
2369
2370 switch (info.colorType()) {
2371 case kAlpha_8_SkColorType:
2372 case kRGB_565_SkColorType:
2373 case kPMColor_SkColorType:
2374 break;
2375 default:
2376 return false;
2377 }
2378
2379 return true;
2380}
2381
2382SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2383 if (!supported_for_raster_canvas(info)) {
2384 return NULL;
2385 }
2386
2387 SkBitmap bitmap;
2388 if (!bitmap.allocPixels(info)) {
2389 return NULL;
2390 }
2391
2392 // should this functionality be moved into allocPixels()?
2393 if (!bitmap.info().isOpaque()) {
2394 bitmap.eraseColor(0);
2395 }
2396 return SkNEW_ARGS(SkCanvas, (bitmap));
2397}