blob: d889174a2aa2271800e4743f96c5ad82e18bc4a0 [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;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000493 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494
495 fMCRec = (MCRec*)fMCStack.push_back();
496 new (fMCRec) MCRec(NULL, 0);
497
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000498 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 fMCRec->fTopLayer = fMCRec->fLayer;
500 fMCRec->fNext = NULL;
501
reed@google.com97af1a62012-08-28 12:19:02 +0000502 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000503
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000504 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505}
506
reed@google.comcde92112011-07-06 20:00:52 +0000507SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000508 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
509{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000510 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000511
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000512 this->init(NULL);
513}
514
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000515SkCanvas::SkCanvas(int width, int height)
516 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
517{
518 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000519
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000520 SkBitmap bitmap;
reed@google.com900ecf22014-02-20 20:55:37 +0000521 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000522 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
523}
524
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000525SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
527{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 inc_canvas();
529
530 this->init(device);
531}
532
533SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
535{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 inc_canvas();
537
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000538 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539}
540
541SkCanvas::~SkCanvas() {
542 // free up the contents of our deque
543 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000544 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000545
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 this->internalRestore(); // restore the last, since we're going away
547
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000548 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000549 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000550
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 dec_canvas();
552}
553
554SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
555 SkRefCnt_SafeAssign(fBounder, bounder);
556 return bounder;
557}
558
559SkDrawFilter* SkCanvas::getDrawFilter() const {
560 return fMCRec->fFilter;
561}
562
563SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
564 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
565 return filter;
566}
567
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000568SkMetaData& SkCanvas::getMetaData() {
569 // metadata users are rare, so we lazily allocate it. If that changes we
570 // can decide to just make it a field in the device (rather than a ptr)
571 if (NULL == fMetaData) {
572 fMetaData = new SkMetaData;
573 }
574 return *fMetaData;
575}
576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577///////////////////////////////////////////////////////////////////////////////
578
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000579void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000580 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000581 if (device) {
582 device->flush();
583 }
584}
585
reed@google.com210ce002011-11-01 14:24:23 +0000586SkISize SkCanvas::getDeviceSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000587 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000588 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
589}
590
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000591SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000593 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkASSERT(rec && rec->fLayer);
595 return rec->fLayer->fDevice;
596}
597
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000598SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000599 if (updateMatrixClip) {
600 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
601 }
reed@google.com9266fed2011-03-30 00:18:03 +0000602 return fMCRec->fTopLayer->fDevice;
603}
604
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000605SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000607 SkDeque::F2BIter iter(fMCStack);
608 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000610 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
612 if (rootDevice == device) {
613 return device;
614 }
reed@google.com4b226022011-01-11 18:32:13 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000617 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 }
619 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000620 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 }
622
623 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
624 rootDevice = device;
625
626 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000627
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 /* Now we update our initial region to have the bounds of the new device,
629 and then intersect all of the clips in our stack with these bounds,
630 to ensure that we can't draw outside of the device's bounds (and trash
631 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000632
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 NOTE: this is only a partial-fix, since if the new device is larger than
634 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000635 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
637 reconstruct the correct clips, so this approximation will have to do.
638 The caller really needs to restore() back to the base if they want to
639 accurately take advantage of the new device bounds.
640 */
641
reed@google.com42aea282012-03-28 16:19:15 +0000642 SkIRect bounds;
643 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000645 } else {
646 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 }
reed@google.com42aea282012-03-28 16:19:15 +0000648 // now jam our 1st clip to be bounds, and intersect the rest with that
649 rec->fRasterClip->setRect(bounds);
650 while ((rec = (MCRec*)iter.next()) != NULL) {
651 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
652 }
653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 return device;
655}
656
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000657bool SkCanvas::readPixels(SkBitmap* bitmap,
658 int x, int y,
659 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000660 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000661 if (!device) {
662 return false;
663 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000664 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000665}
666
bsalomon@google.comc6980972011-11-02 19:57:21 +0000667bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000668 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000669 if (!device) {
670 return false;
671 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000672
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000673 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000674 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000675 if (!bounds.intersect(srcRect)) {
676 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000677 }
678
679 SkBitmap tmp;
680 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
681 bounds.height());
682 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
683 bitmap->swap(tmp);
684 return true;
685 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000686 return false;
687 }
reed@google.com51df9e32010-12-23 19:29:18 +0000688}
689
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000690void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
691 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000692 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000693 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000694 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
695 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
696 device->accessBitmap(true);
697 device->writePixels(bitmap, x, y, config8888);
698 }
reed@google.com51df9e32010-12-23 19:29:18 +0000699 }
700}
701
junov@google.com4370aed2012-01-18 16:21:08 +0000702SkCanvas* SkCanvas::canvasForDrawIter() {
703 return this;
704}
705
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706//////////////////////////////////////////////////////////////////////////////
707
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708void SkCanvas::updateDeviceCMCache() {
709 if (fDeviceCMDirty) {
710 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000711 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000713
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000715 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000717 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000719 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 } while ((layer = layer->fNext) != NULL);
721 }
722 fDeviceCMDirty = false;
723 }
724}
725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726///////////////////////////////////////////////////////////////////////////////
727
728int SkCanvas::internalSave(SaveFlags flags) {
729 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000730
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 MCRec* newTop = (MCRec*)fMCStack.push_back();
732 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 newTop->fNext = fMCRec;
735 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000736
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000737 if (SkCanvas::kClip_SaveFlag & flags) {
738 fClipStack.save();
739 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000740
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 return saveCount;
742}
743
744int SkCanvas::save(SaveFlags flags) {
745 // call shared impl
746 return this->internalSave(flags);
747}
748
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
750 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
751}
752
junov@chromium.orga907ac32012-02-24 21:54:07 +0000753bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000754 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000755 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000756 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000757 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000758 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000759 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000760
761 if (imageFilter) {
762 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
763 // Filters may grow the bounds beyond the device bounds.
764 op = SkRegion::kReplace_Op;
765 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000766 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 if (NULL != bounds) {
768 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000769
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 this->getTotalMatrix().mapRect(&r, *bounds);
771 r.roundOut(&ir);
772 // early exit if the layer's bounds are clipped out
773 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000774 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000775 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000776 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000777 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 }
779 } else { // no user bounds, so just use the clip
780 ir = clipBounds;
781 }
782
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000783 fClipStack.clipDevRect(ir, op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 // early exit if the clip is now empty
786 if (bounds_affects_clip(flags) &&
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000787 !fMCRec->fRasterClip->op(ir, op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000788 return false;
789 }
790
791 if (intersection) {
792 *intersection = ir;
793 }
794 return true;
795}
796
797int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
798 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000799 return this->internalSaveLayer(bounds, paint, flags, false);
800}
801
reed@google.com76f10a32014-02-05 15:32:21 +0000802static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000803 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000804 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000805 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000806}
807
reed@google.com8926b162012-03-23 15:36:36 +0000808int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
809 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 // do this before we create the layer. We don't call the public save() since
811 // that would invoke a possibly overridden virtual
812 int count = this->internalSave(flags);
813
814 fDeviceCMDirty = true;
815
816 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000817 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 return count;
819 }
820
reed@google.comb55deeb2012-01-06 14:43:09 +0000821 // Kill the imagefilter if our device doesn't allow it
822 SkLazyPaint lazyP;
823 if (paint && paint->getImageFilter()) {
824 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000825 if (justForImageFilter) {
826 // early exit if the layer was just for the imageFilter
827 return count;
828 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000829 SkPaint* p = lazyP.set(*paint);
830 p->setImageFilter(NULL);
831 paint = p;
832 }
833 }
834
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000835 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
836 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
837 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000839 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000840 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000841 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000842 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000843 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000844 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000845 if (NULL == device) {
846 SkDebugf("Unable to create device for layer.");
847 return count;
848 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000849
reed@google.com6f8f2922011-03-04 22:27:10 +0000850 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000851 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 device->unref();
853
854 layer->fNext = fMCRec->fTopLayer;
855 fMCRec->fLayer = layer;
856 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
857
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000858 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 return count;
860}
861
862int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
863 SaveFlags flags) {
864 if (0xFF == alpha) {
865 return this->saveLayer(bounds, NULL, flags);
866 } else {
867 SkPaint tmpPaint;
868 tmpPaint.setAlpha(alpha);
869 return this->saveLayer(bounds, &tmpPaint, flags);
870 }
871}
872
873void SkCanvas::restore() {
874 // check for underflow
875 if (fMCStack.count() > 1) {
876 this->internalRestore();
877 }
878}
879
880void SkCanvas::internalRestore() {
881 SkASSERT(fMCStack.count() != 0);
882
883 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000884 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000886 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
887 fClipStack.restore();
888 }
889
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000890 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 DeviceCM* layer = fMCRec->fLayer; // may be null
892 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
893 fMCRec->fLayer = NULL;
894
895 // now do the normal restore()
896 fMCRec->~MCRec(); // balanced in save()
897 fMCStack.pop_back();
898 fMCRec = (MCRec*)fMCStack.back();
899
900 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
901 since if we're being recorded, we don't want to record this (the
902 recorder will have already recorded the restore).
903 */
904 if (NULL != layer) {
905 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000906 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000907 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
908 layer->fPaint);
909 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000911
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000912 SkASSERT(fSaveLayerCount > 0);
913 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 }
915 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000916 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917}
918
919int SkCanvas::getSaveCount() const {
920 return fMCStack.count();
921}
922
923void SkCanvas::restoreToCount(int count) {
924 // sanity check
925 if (count < 1) {
926 count = 1;
927 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000928
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000929 int n = this->getSaveCount() - count;
930 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 this->restore();
932 }
933}
934
reed@google.com7c202932011-12-14 18:48:05 +0000935bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000936 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000937}
938
reed@google.com76f10a32014-02-05 15:32:21 +0000939SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
940 return this->onNewSurface(info);
941}
942
943SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
944 SkBaseDevice* dev = this->getDevice();
945 return dev ? dev->newSurface(info) : NULL;
946}
947
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000948SkImageInfo SkCanvas::imageInfo() const {
949 SkBaseDevice* dev = this->getDevice();
950 if (dev) {
951 return dev->imageInfo();
952 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000953 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000954 }
955}
956
957const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
958 return this->onPeekPixels(info, rowBytes);
959}
960
961const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
962 SkBaseDevice* dev = this->getDevice();
963 return dev ? dev->peekPixels(info, rowBytes) : NULL;
964}
965
966SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
967 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
968 if (NULL == fAddr) {
969 fInfo = canvas->imageInfo();
970 if (kUnknown_SkColorType == fInfo.colorType() ||
971 !fBitmap.allocPixels(fInfo))
972 {
973 return; // failure, fAddr is NULL
974 }
975 fBitmap.lockPixels();
976 if (!canvas->readPixels(&fBitmap, 0, 0)) {
977 return; // failure, fAddr is NULL
978 }
979 fAddr = fBitmap.getPixels();
980 fRowBytes = fBitmap.rowBytes();
981 }
982 SkASSERT(fAddr); // success
983}
984
985bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
986 if (fAddr) {
987 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
988 NULL, NULL);
989 } else {
990 bitmap->reset();
991 return false;
992 }
993}
994
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995/////////////////////////////////////////////////////////////////////////////
996
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000997void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000999 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 return;
1001 }
1002
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001003 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001005 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001007
1008 SkDEBUGCODE(bitmap.validate();)
1009 CHECK_LOCKCOUNT_BALANCE(bitmap);
1010
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001011 SkRect storage;
1012 const SkRect* bounds = NULL;
1013 if (paint && paint->canComputeFastBounds()) {
1014 bitmap.getBounds(&storage);
1015 matrix.mapRect(&storage);
1016 bounds = &paint->computeFastBounds(storage, &storage);
1017 }
1018
1019 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001020
1021 while (iter.next()) {
1022 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1023 }
1024
1025 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026}
1027
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001028void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001029 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 SkPaint tmp;
1031 if (NULL == paint) {
1032 tmp.setDither(true);
1033 paint = &tmp;
1034 }
reed@google.com4b226022011-01-11 18:32:13 +00001035
reed@google.com8926b162012-03-23 15:36:36 +00001036 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001038 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001039 paint = &looper.paint();
1040 SkImageFilter* filter = paint->getImageFilter();
1041 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001042 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001043 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001044 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001045 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001046 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001047 SkMatrix matrix = *iter.fMatrix;
1048 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001049 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001050 SkPaint tmpUnfiltered(*paint);
1051 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001052 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1053 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001054 }
1055 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001056 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001057 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001059 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060}
1061
reed@google.com8926b162012-03-23 15:36:36 +00001062void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1063 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001064 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001065 return;
1066 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001067 SkDEBUGCODE(bitmap.validate();)
1068 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001069
reed@google.com8926b162012-03-23 15:36:36 +00001070 SkPaint tmp;
1071 if (NULL == paint) {
1072 paint = &tmp;
1073 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001074
reed@google.com8926b162012-03-23 15:36:36 +00001075 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001076
reed@google.com8926b162012-03-23 15:36:36 +00001077 while (iter.next()) {
1078 paint = &looper.paint();
1079 SkImageFilter* filter = paint->getImageFilter();
1080 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1081 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001082 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001083 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001084 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001085 SkMatrix matrix = *iter.fMatrix;
1086 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001087 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001088 SkPaint tmpUnfiltered(*paint);
1089 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001090 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001091 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001092 }
1093 } else {
1094 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1095 }
1096 }
1097 LOOPER_END
1098}
1099
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100/////////////////////////////////////////////////////////////////////////////
1101
1102bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1103 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001104 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 return fMCRec->fMatrix->preTranslate(dx, dy);
1106}
1107
1108bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1109 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001110 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 return fMCRec->fMatrix->preScale(sx, sy);
1112}
1113
1114bool SkCanvas::rotate(SkScalar degrees) {
1115 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001116 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 return fMCRec->fMatrix->preRotate(degrees);
1118}
1119
1120bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1121 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001122 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 return fMCRec->fMatrix->preSkew(sx, sy);
1124}
1125
1126bool SkCanvas::concat(const SkMatrix& matrix) {
1127 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001128 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 return fMCRec->fMatrix->preConcat(matrix);
1130}
1131
1132void SkCanvas::setMatrix(const SkMatrix& matrix) {
1133 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001134 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 *fMCRec->fMatrix = matrix;
1136}
1137
1138// this is not virtual, so it must call a virtual method so that subclasses
1139// will see its action
1140void SkCanvas::resetMatrix() {
1141 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001142
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 matrix.reset();
1144 this->setMatrix(matrix);
1145}
1146
1147//////////////////////////////////////////////////////////////////////////////
1148
reed@google.comc42d35d2011-10-12 11:57:42 +00001149bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001150#ifdef SK_ENABLE_CLIP_QUICKREJECT
1151 if (SkRegion::kIntersect_Op == op) {
1152 if (fMCRec->fRasterClip->isEmpty()) {
1153 return false;
1154 }
1155
reed@google.com3b3e8952012-08-16 20:53:31 +00001156 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001157 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001158 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001159
1160 fClipStack.clipEmpty();
1161 return fMCRec->fRasterClip->setEmpty();
1162 }
1163 }
1164#endif
1165
reed@google.com5c3d1472011-02-22 19:12:23 +00001166 AutoValidateClip avc(this);
1167
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001169 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001170 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
1172 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001173 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001174 // the matrix. This means we don't have to a) make a path, and b) tell
1175 // the region code to scan-convert the path, only to discover that it
1176 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178
1179 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001180 fClipStack.clipDevRect(r, op, doAA);
1181 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001183 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001184 // and clip against that, since it can handle any matrix. However, to
1185 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1186 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 SkPath path;
1188
1189 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001190 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 }
1192}
1193
reed@google.com00177082011-10-12 14:34:30 +00001194static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001195 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001196 // base is used to limit the size (and therefore memory allocation) of the
1197 // region that results from scan converting devPath.
1198 SkRegion base;
1199
reed@google.com819c9212011-02-23 18:56:55 +00001200 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001201 // since we are intersect, we can do better (tighter) with currRgn's
1202 // bounds, than just using the device. However, if currRgn is complex,
1203 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001204 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001205 // FIXME: we should also be able to do this when currClip->isBW(),
1206 // but relaxing the test above triggers GM asserts in
1207 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1208 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001209 } else {
reed@google.com00177082011-10-12 14:34:30 +00001210 base.setRect(currClip->getBounds());
1211 SkRasterClip clip;
1212 clip.setPath(devPath, base, doAA);
1213 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001214 }
reed@google.com819c9212011-02-23 18:56:55 +00001215 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001216 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001217 if (!device) {
1218 return currClip->setEmpty();
1219 }
1220
junov@chromium.orga907ac32012-02-24 21:54:07 +00001221 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001222
1223 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001224 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001225 } else {
reed@google.com00177082011-10-12 14:34:30 +00001226 SkRasterClip clip;
1227 clip.setPath(devPath, base, doAA);
1228 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001229 }
1230 }
1231}
1232
reed@google.com4ed0fb72012-12-12 20:48:18 +00001233bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1234 if (rrect.isRect()) {
1235 // call the non-virtual version
1236 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001237 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001238
1239 SkRRect transformedRRect;
1240 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1241 AutoValidateClip avc(this);
1242
1243 fDeviceCMDirty = true;
1244 fCachedLocalClipBoundsDirty = true;
1245 doAA &= fAllowSoftClip;
1246
1247 fClipStack.clipDevRRect(transformedRRect, op, doAA);
1248
1249 SkPath devPath;
1250 devPath.addRRect(transformedRRect);
1251
1252 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
1253 }
1254
1255 SkPath path;
1256 path.addRRect(rrect);
1257 // call the non-virtual version
1258 return this->SkCanvas::clipPath(path, op, doAA);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001259}
1260
reed@google.comc42d35d2011-10-12 11:57:42 +00001261bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001262#ifdef SK_ENABLE_CLIP_QUICKREJECT
1263 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1264 if (fMCRec->fRasterClip->isEmpty()) {
1265 return false;
1266 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001267
reed@google.com3b3e8952012-08-16 20:53:31 +00001268 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001269 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001270 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001271
reed@google.comda17f752012-08-16 18:27:05 +00001272 fClipStack.clipEmpty();
1273 return fMCRec->fRasterClip->setEmpty();
1274 }
1275 }
1276#endif
1277
reed@google.com5c3d1472011-02-22 19:12:23 +00001278 AutoValidateClip avc(this);
1279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001281 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001282 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283
1284 SkPath devPath;
1285 path.transform(*fMCRec->fMatrix, &devPath);
1286
reed@google.comfe701122011-11-08 19:41:23 +00001287 // Check if the transfomation, or the original path itself
1288 // made us empty. Note this can also happen if we contained NaN
1289 // values. computing the bounds detects this, and will set our
1290 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1291 if (devPath.getBounds().isEmpty()) {
1292 // resetting the path will remove any NaN or other wanky values
1293 // that might upset our scan converter.
1294 devPath.reset();
1295 }
1296
reed@google.com5c3d1472011-02-22 19:12:23 +00001297 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001298 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001299
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001300 if (fAllowSimplifyClip) {
1301 devPath.reset();
1302 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1303 const SkClipStack* clipStack = getClipStack();
1304 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1305 const SkClipStack::Element* element;
1306 while ((element = iter.next())) {
1307 SkClipStack::Element::Type type = element->getType();
1308 if (type == SkClipStack::Element::kEmpty_Type) {
1309 continue;
1310 }
1311 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001312 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001313 SkRegion::Op elementOp = element->getOp();
1314 if (elementOp == SkRegion::kReplace_Op) {
1315 devPath = operand;
1316 } else {
1317 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1318 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001319 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1320 // perhaps we need an API change to avoid this sort of mixed-signals about
1321 // clipping.
1322 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001323 }
1324 op = SkRegion::kReplace_Op;
1325 }
1326
reed@google.com00177082011-10-12 14:34:30 +00001327 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328}
1329
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001330bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1331 bool inverseFilled) {
1332 // This is for updating the clip conservatively using only bounds
1333 // information.
1334 // Contract:
1335 // The current clip must contain the true clip. The true
1336 // clip is the clip that would have normally been computed
1337 // by calls to clipPath and clipRRect
1338 // Objective:
1339 // Keep the current clip as small as possible without
1340 // breaking the contract, using only clip bounding rectangles
1341 // (for performance).
1342
1343 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1344 // don't have to worry about getting caught in a loop. Thus anywhere
1345 // we call a virtual method, we explicitly prefix it with
1346 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001347
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001348 if (inverseFilled) {
1349 switch (op) {
1350 case SkRegion::kIntersect_Op:
1351 case SkRegion::kDifference_Op:
1352 // These ops can only shrink the current clip. So leaving
1353 // the clip unchanges conservatively respects the contract.
1354 return this->getClipDeviceBounds(NULL);
1355 case SkRegion::kUnion_Op:
1356 case SkRegion::kReplace_Op:
1357 case SkRegion::kReverseDifference_Op:
1358 case SkRegion::kXOR_Op:
1359 {
1360 // These ops can grow the current clip up to the extents of
1361 // the input clip, which is inverse filled, so we just set
1362 // the current clip to the device bounds.
1363 SkRect deviceBounds;
1364 SkIRect deviceIBounds;
1365 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001366 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001367 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1368 // set the clip in device space
1369 this->SkCanvas::setMatrix(SkMatrix::I());
1370 bool result = this->SkCanvas::clipRect(deviceBounds,
1371 SkRegion::kReplace_Op, false);
1372 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1373 return result;
1374 }
1375 default:
1376 SkASSERT(0); // unhandled op?
1377 }
1378 } else {
1379 // Not inverse filled
1380 switch (op) {
1381 case SkRegion::kIntersect_Op:
1382 case SkRegion::kUnion_Op:
1383 case SkRegion::kReplace_Op:
1384 return this->SkCanvas::clipRect(bounds, op, false);
1385 case SkRegion::kDifference_Op:
1386 // Difference can only shrink the current clip.
1387 // Leaving clip unchanged conservatively fullfills the contract.
1388 return this->getClipDeviceBounds(NULL);
1389 case SkRegion::kReverseDifference_Op:
1390 // To reverse, we swap in the bounds with a replace op.
1391 // As with difference, leave it unchanged.
1392 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1393 case SkRegion::kXOR_Op:
1394 // Be conservative, based on (A XOR B) always included in (A union B),
1395 // which is always included in (bounds(A) union bounds(B))
1396 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1397 default:
1398 SkASSERT(0); // unhandled op?
1399 }
1400 }
1401 return true;
1402}
1403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001405 AutoValidateClip avc(this);
1406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001408 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409
reed@google.com5c3d1472011-02-22 19:12:23 +00001410 // todo: signal fClipStack that we have a region, and therefore (I guess)
1411 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001412 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001413
reed@google.com00177082011-10-12 14:34:30 +00001414 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415}
1416
reed@google.com819c9212011-02-23 18:56:55 +00001417#ifdef SK_DEBUG
1418void SkCanvas::validateClip() const {
1419 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001420 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001421 if (!device) {
1422 SkASSERT(this->getTotalClip().isEmpty());
1423 return;
1424 }
1425
reed@google.com819c9212011-02-23 18:56:55 +00001426 SkIRect ir;
1427 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001428 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001429
robertphillips@google.com80214e22012-07-20 15:33:18 +00001430 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001431 const SkClipStack::Element* element;
1432 while ((element = iter.next()) != NULL) {
1433 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001434 case SkClipStack::Element::kRect_Type:
1435 element->getRect().round(&ir);
1436 tmpClip.op(ir, element->getOp());
1437 break;
1438 case SkClipStack::Element::kEmpty_Type:
1439 tmpClip.setEmpty();
1440 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001441 default: {
1442 SkPath path;
1443 element->asPath(&path);
1444 clipPathHelper(this,
1445 &tmpClip,
1446 path,
1447 element->getOp(),
1448 element->isAA());
1449 break;
1450 }
reed@google.com819c9212011-02-23 18:56:55 +00001451 }
1452 }
1453
reed@google.com6f8f2922011-03-04 22:27:10 +00001454#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001455 // now compare against the current rgn
1456 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001457 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001458#endif
reed@google.com819c9212011-02-23 18:56:55 +00001459}
1460#endif
1461
reed@google.com90c07ea2012-04-13 13:50:27 +00001462void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001463 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001464 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001465
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001466 static const SkRect kEmpty = { 0, 0, 0, 0 };
1467 while ((element = iter.next()) != NULL) {
1468 switch (element->getType()) {
1469 case SkClipStack::Element::kPath_Type:
1470 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1471 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001472 case SkClipStack::Element::kRRect_Type:
1473 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1474 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001475 case SkClipStack::Element::kRect_Type:
1476 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1477 break;
1478 case SkClipStack::Element::kEmpty_Type:
1479 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1480 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001481 }
1482 }
1483}
1484
reed@google.com5c3d1472011-02-22 19:12:23 +00001485///////////////////////////////////////////////////////////////////////////////
1486
reed@google.com3b3e8952012-08-16 20:53:31 +00001487bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001488
reed@google.com16078632011-12-06 18:56:37 +00001489 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001490 return true;
1491
reed@google.com00177082011-10-12 14:34:30 +00001492 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 return true;
1494 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495
tomhudson@google.com8d430182011-06-06 19:11:19 +00001496 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001497 SkRect dst;
1498 fMCRec->fMatrix->mapRect(&dst, rect);
1499 SkIRect idst;
1500 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001501 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001502 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001503 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001504
reed@android.coma380ae42009-07-21 01:17:02 +00001505 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001506 // TODO: should we use | instead, or compare all 4 at once?
1507 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001508 return true;
1509 }
reed@google.comc0784db2013-12-13 21:16:12 +00001510 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001511 return true;
1512 }
1513 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515}
1516
reed@google.com3b3e8952012-08-16 20:53:31 +00001517bool SkCanvas::quickReject(const SkPath& path) const {
1518 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519}
1520
reed@google.com3b3e8952012-08-16 20:53:31 +00001521bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001522 SkIRect ibounds;
1523 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 return false;
1525 }
1526
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001527 SkMatrix inverse;
1528 // if we can't invert the CTM, we can't return local clip bounds
1529 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001530 if (bounds) {
1531 bounds->setEmpty();
1532 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001533 return false;
1534 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001536 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001537 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001538 // adjust it outwards in case we are antialiasing
1539 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001540
reed@google.com8f4d2302013-12-17 16:44:46 +00001541 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1542 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543 inverse.mapRect(bounds, r);
1544 }
1545 return true;
1546}
1547
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001548bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001549 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001550 if (clip.isEmpty()) {
1551 if (bounds) {
1552 bounds->setEmpty();
1553 }
1554 return false;
1555 }
1556
1557 if (NULL != bounds) {
1558 *bounds = clip.getBounds();
1559 }
1560 return true;
1561}
1562
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563const SkMatrix& SkCanvas::getTotalMatrix() const {
1564 return *fMCRec->fMatrix;
1565}
1566
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001567SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001568 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1569 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001570 return kComplex_ClipType;
1571}
1572
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001574 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001577SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001578 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001579 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580}
1581
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001582GrContext* SkCanvas::getGrContext() {
1583#if SK_SUPPORT_GPU
1584 SkBaseDevice* device = this->getTopDevice();
1585 if (NULL != device) {
1586 GrRenderTarget* renderTarget = device->accessRenderTarget();
1587 if (NULL != renderTarget) {
1588 return renderTarget->getContext();
1589 }
1590 }
1591#endif
1592
1593 return NULL;
1594
1595}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001596
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001597void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1598 const SkPaint& paint) {
1599 if (outer.isEmpty()) {
1600 return;
1601 }
1602 if (inner.isEmpty()) {
1603 this->drawRRect(outer, paint);
1604 return;
1605 }
1606
1607 // We don't have this method (yet), but technically this is what we should
1608 // be able to assert...
1609 // SkASSERT(outer.contains(inner));
1610 //
1611 // For now at least check for containment of bounds
1612 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1613
1614 this->onDrawDRRect(outer, inner, paint);
1615}
1616
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617//////////////////////////////////////////////////////////////////////////////
1618// These are the virtual drawing methods
1619//////////////////////////////////////////////////////////////////////////////
1620
reed@google.com2a981812011-04-14 18:59:28 +00001621void SkCanvas::clear(SkColor color) {
1622 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001623 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001624 while (iter.next()) {
1625 iter.fDevice->clear(color);
1626 }
1627}
1628
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001630 this->internalDrawPaint(paint);
1631}
1632
1633void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001634 CHECK_SHADER_NOSETCONTEXT(paint);
1635
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001636 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637
1638 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001639 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640 }
1641
reed@google.com4e2b3d32011-04-07 14:18:59 +00001642 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643}
1644
1645void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1646 const SkPaint& paint) {
1647 if ((long)count <= 0) {
1648 return;
1649 }
1650
reed@google.comea033602012-12-14 13:13:55 +00001651 CHECK_SHADER_NOSETCONTEXT(paint);
1652
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001653 SkRect r, storage;
1654 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001655 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001656 // special-case 2 points (common for drawing a single line)
1657 if (2 == count) {
1658 r.set(pts[0], pts[1]);
1659 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001660 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001661 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001662 bounds = &paint.computeFastStrokeBounds(r, &storage);
1663 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001664 return;
1665 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001666 }
reed@google.coma584aed2012-05-16 14:06:02 +00001667
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668 SkASSERT(pts != NULL);
1669
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001670 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001671
reed@android.com8a1c16f2008-12-17 15:59:43 +00001672 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001673 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 }
reed@google.com4b226022011-01-11 18:32:13 +00001675
reed@google.com4e2b3d32011-04-07 14:18:59 +00001676 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677}
1678
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001679void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001680 CHECK_SHADER_NOSETCONTEXT(paint);
1681
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001682 SkRect storage;
1683 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001685 bounds = &paint.computeFastBounds(r, &storage);
1686 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687 return;
1688 }
1689 }
reed@google.com4b226022011-01-11 18:32:13 +00001690
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001691 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692
1693 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001694 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 }
1696
reed@google.com4e2b3d32011-04-07 14:18:59 +00001697 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001698}
1699
reed@google.com4ed0fb72012-12-12 20:48:18 +00001700void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001701 CHECK_SHADER_NOSETCONTEXT(paint);
1702
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001703 SkRect storage;
1704 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001705 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001706 bounds = &paint.computeFastBounds(oval, &storage);
1707 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001708 return;
1709 }
1710 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001711
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001712 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001713
1714 while (iter.next()) {
1715 iter.fDevice->drawOval(iter, oval, looper.paint());
1716 }
1717
1718 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001719}
1720
1721void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001722 CHECK_SHADER_NOSETCONTEXT(paint);
1723
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001724 SkRect storage;
1725 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001726 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001727 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1728 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001729 return;
1730 }
1731 }
1732
1733 if (rrect.isRect()) {
1734 // call the non-virtual version
1735 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001736 return;
1737 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001738 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001739 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1740 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001741 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001742
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001743 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001744
1745 while (iter.next()) {
1746 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1747 }
1748
1749 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001750}
1751
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001752void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1753 const SkPaint& paint) {
1754 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001755
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001756 SkRect storage;
1757 const SkRect* bounds = NULL;
1758 if (paint.canComputeFastBounds()) {
1759 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1760 if (this->quickReject(*bounds)) {
1761 return;
1762 }
1763 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001764
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001765 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001766
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001767 while (iter.next()) {
1768 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1769 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001770
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001771 LOOPER_END
1772}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001773
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001774void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001775 CHECK_SHADER_NOSETCONTEXT(paint);
1776
reed@google.com93645112012-07-26 16:11:47 +00001777 if (!path.isFinite()) {
1778 return;
1779 }
1780
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001781 SkRect storage;
1782 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001783 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001784 const SkRect& pathBounds = path.getBounds();
1785 bounds = &paint.computeFastBounds(pathBounds, &storage);
1786 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 return;
1788 }
1789 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001790
1791 const SkRect& r = path.getBounds();
1792 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001793 if (path.isInverseFillType()) {
1794 this->internalDrawPaint(paint);
1795 }
1796 return;
1797 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001799 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800
1801 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001802 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 }
1804
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806}
1807
1808void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1809 const SkPaint* paint) {
1810 SkDEBUGCODE(bitmap.validate();)
1811
reed@google.com3d608122011-11-21 15:16:16 +00001812 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001813 SkRect bounds = {
1814 x, y,
1815 x + SkIntToScalar(bitmap.width()),
1816 y + SkIntToScalar(bitmap.height())
1817 };
1818 if (paint) {
1819 (void)paint->computeFastBounds(bounds, &bounds);
1820 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001821 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 return;
1823 }
1824 }
reed@google.com4b226022011-01-11 18:32:13 +00001825
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826 SkMatrix matrix;
1827 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001828 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829}
1830
reed@google.com9987ec32011-09-07 11:57:52 +00001831// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001832void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001833 const SkRect& dst, const SkPaint* paint,
1834 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001835 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836 return;
1837 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001838
reed@google.comea033602012-12-14 13:13:55 +00001839 CHECK_LOCKCOUNT_BALANCE(bitmap);
1840
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001841 SkRect storage;
1842 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001843 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001844 if (paint) {
1845 bounds = &paint->computeFastBounds(dst, &storage);
1846 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001847 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001848 return;
1849 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001850 }
reed@google.com3d608122011-11-21 15:16:16 +00001851
reed@google.com33535f32012-09-25 15:37:50 +00001852 SkLazyPaint lazy;
1853 if (NULL == paint) {
1854 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001856
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001857 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001858
reed@google.com33535f32012-09-25 15:37:50 +00001859 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001860 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001861 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001862
reed@google.com33535f32012-09-25 15:37:50 +00001863 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864}
1865
reed@google.com71121732012-09-18 15:14:33 +00001866void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001867 const SkRect& dst, const SkPaint* paint,
1868 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001869 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001870 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001871}
1872
reed@android.com8a1c16f2008-12-17 15:59:43 +00001873void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1874 const SkPaint* paint) {
1875 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001876 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001877}
1878
reed@google.com9987ec32011-09-07 11:57:52 +00001879void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1880 const SkIRect& center, const SkRect& dst,
1881 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001882 if (bitmap.drawsNothing()) {
1883 return;
1884 }
reed@google.com3d608122011-11-21 15:16:16 +00001885 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001886 SkRect storage;
1887 const SkRect* bounds = &dst;
1888 if (paint) {
1889 bounds = &paint->computeFastBounds(dst, &storage);
1890 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001891 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001892 return;
1893 }
1894 }
1895
reed@google.com9987ec32011-09-07 11:57:52 +00001896 const int32_t w = bitmap.width();
1897 const int32_t h = bitmap.height();
1898
1899 SkIRect c = center;
1900 // pin center to the bounds of the bitmap
1901 c.fLeft = SkMax32(0, center.fLeft);
1902 c.fTop = SkMax32(0, center.fTop);
1903 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1904 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1905
reed@google.com71121732012-09-18 15:14:33 +00001906 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001907 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001908 };
1909 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001910 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001911 };
reed@google.com9987ec32011-09-07 11:57:52 +00001912 SkScalar dstX[4] = {
1913 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1914 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1915 };
1916 SkScalar dstY[4] = {
1917 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1918 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1919 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001920
reed@google.com9987ec32011-09-07 11:57:52 +00001921 if (dstX[1] > dstX[2]) {
1922 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1923 dstX[2] = dstX[1];
1924 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001925
reed@google.com9987ec32011-09-07 11:57:52 +00001926 if (dstY[1] > dstY[2]) {
1927 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1928 dstY[2] = dstY[1];
1929 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001930
reed@google.com9987ec32011-09-07 11:57:52 +00001931 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001932 SkRect s, d;
1933
reed@google.com9987ec32011-09-07 11:57:52 +00001934 s.fTop = srcY[y];
1935 s.fBottom = srcY[y+1];
1936 d.fTop = dstY[y];
1937 d.fBottom = dstY[y+1];
1938 for (int x = 0; x < 3; x++) {
1939 s.fLeft = srcX[x];
1940 s.fRight = srcX[x+1];
1941 d.fLeft = dstX[x];
1942 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001943 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001944 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001945 }
1946 }
1947}
1948
1949void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1950 const SkRect& dst, const SkPaint* paint) {
1951 SkDEBUGCODE(bitmap.validate();)
1952
1953 // Need a device entry-point, so gpu can use a mesh
1954 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1955}
1956
reed@google.comf67e4cf2011-03-15 20:56:58 +00001957class SkDeviceFilteredPaint {
1958public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001959 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1960 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001961 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001962 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001963 newPaint->setFlags(flags.fFlags);
1964 newPaint->setHinting(flags.fHinting);
1965 fPaint = newPaint;
1966 } else {
1967 fPaint = &paint;
1968 }
1969 }
1970
reed@google.comf67e4cf2011-03-15 20:56:58 +00001971 const SkPaint& paint() const { return *fPaint; }
1972
1973private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001974 const SkPaint* fPaint;
1975 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001976};
1977
bungeman@google.com52c748b2011-08-22 21:30:43 +00001978void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1979 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001980 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001981 draw.fDevice->drawRect(draw, r, paint);
1982 } else {
1983 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001984 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001985 draw.fDevice->drawRect(draw, r, p);
1986 }
1987}
1988
1989void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1990 const char text[], size_t byteLength,
1991 SkScalar x, SkScalar y) {
1992 SkASSERT(byteLength == 0 || text != NULL);
1993
1994 // nothing to draw
1995 if (text == NULL || byteLength == 0 ||
1996 draw.fClip->isEmpty() ||
1997 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1998 return;
1999 }
2000
2001 SkScalar width = 0;
2002 SkPoint start;
2003
2004 start.set(0, 0); // to avoid warning
2005 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2006 SkPaint::kStrikeThruText_Flag)) {
2007 width = paint.measureText(text, byteLength);
2008
2009 SkScalar offsetX = 0;
2010 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2011 offsetX = SkScalarHalf(width);
2012 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2013 offsetX = width;
2014 }
2015 start.set(x - offsetX, y);
2016 }
2017
2018 if (0 == width) {
2019 return;
2020 }
2021
2022 uint32_t flags = paint.getFlags();
2023
2024 if (flags & (SkPaint::kUnderlineText_Flag |
2025 SkPaint::kStrikeThruText_Flag)) {
2026 SkScalar textSize = paint.getTextSize();
2027 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2028 SkRect r;
2029
2030 r.fLeft = start.fX;
2031 r.fRight = start.fX + width;
2032
2033 if (flags & SkPaint::kUnderlineText_Flag) {
2034 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2035 start.fY);
2036 r.fTop = offset;
2037 r.fBottom = offset + height;
2038 DrawRect(draw, paint, r, textSize);
2039 }
2040 if (flags & SkPaint::kStrikeThruText_Flag) {
2041 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2042 start.fY);
2043 r.fTop = offset;
2044 r.fBottom = offset + height;
2045 DrawRect(draw, paint, r, textSize);
2046 }
2047 }
2048}
2049
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050void SkCanvas::drawText(const void* text, size_t byteLength,
2051 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002052 CHECK_SHADER_NOSETCONTEXT(paint);
2053
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002054 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055
2056 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002057 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002058 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002059 DrawTextDecorations(iter, dfp.paint(),
2060 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 }
2062
reed@google.com4e2b3d32011-04-07 14:18:59 +00002063 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064}
2065
2066void SkCanvas::drawPosText(const void* text, size_t byteLength,
2067 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002068 CHECK_SHADER_NOSETCONTEXT(paint);
2069
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002070 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002071
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002073 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002074 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002075 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076 }
reed@google.com4b226022011-01-11 18:32:13 +00002077
reed@google.com4e2b3d32011-04-07 14:18:59 +00002078 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002079}
2080
2081void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2082 const SkScalar xpos[], SkScalar constY,
2083 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002084 CHECK_SHADER_NOSETCONTEXT(paint);
2085
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002086 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002087
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002089 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002091 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 }
reed@google.com4b226022011-01-11 18:32:13 +00002093
reed@google.com4e2b3d32011-04-07 14:18:59 +00002094 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095}
2096
2097void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2098 const SkPath& path, const SkMatrix* matrix,
2099 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002100 CHECK_SHADER_NOSETCONTEXT(paint);
2101
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002102 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103
2104 while (iter.next()) {
2105 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002106 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107 }
2108
reed@google.com4e2b3d32011-04-07 14:18:59 +00002109 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002110}
2111
2112void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2113 const SkPoint verts[], const SkPoint texs[],
2114 const SkColor colors[], SkXfermode* xmode,
2115 const uint16_t indices[], int indexCount,
2116 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002117 CHECK_SHADER_NOSETCONTEXT(paint);
2118
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002119 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002120
reed@android.com8a1c16f2008-12-17 15:59:43 +00002121 while (iter.next()) {
2122 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002123 colors, xmode, indices, indexCount,
2124 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125 }
reed@google.com4b226022011-01-11 18:32:13 +00002126
reed@google.com4e2b3d32011-04-07 14:18:59 +00002127 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128}
2129
2130//////////////////////////////////////////////////////////////////////////////
2131// These methods are NOT virtual, and therefore must call back into virtual
2132// methods, rather than actually drawing themselves.
2133//////////////////////////////////////////////////////////////////////////////
2134
2135void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002136 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137 SkPaint paint;
2138
2139 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002140 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002141 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 }
2143 this->drawPaint(paint);
2144}
2145
reed@android.com845fdac2009-06-23 03:01:32 +00002146void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 SkPaint paint;
2148
2149 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002150 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002151 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152 }
2153 this->drawPaint(paint);
2154}
2155
2156void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2157 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002158
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 pt.set(x, y);
2160 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2161}
2162
2163void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2164 SkPoint pt;
2165 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002166
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 pt.set(x, y);
2168 paint.setColor(color);
2169 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2170}
2171
2172void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2173 const SkPaint& paint) {
2174 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002175
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176 pts[0].set(x0, y0);
2177 pts[1].set(x1, y1);
2178 this->drawPoints(kLines_PointMode, 2, pts, paint);
2179}
2180
2181void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2182 SkScalar right, SkScalar bottom,
2183 const SkPaint& paint) {
2184 SkRect r;
2185
2186 r.set(left, top, right, bottom);
2187 this->drawRect(r, paint);
2188}
2189
2190void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2191 const SkPaint& paint) {
2192 if (radius < 0) {
2193 radius = 0;
2194 }
2195
2196 SkRect r;
2197 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002198 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199}
2200
2201void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2202 const SkPaint& paint) {
2203 if (rx > 0 && ry > 0) {
2204 if (paint.canComputeFastBounds()) {
2205 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002206 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 return;
2208 }
2209 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002210 SkRRect rrect;
2211 rrect.setRectXY(r, rx, ry);
2212 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213 } else {
2214 this->drawRect(r, paint);
2215 }
2216}
2217
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2219 SkScalar sweepAngle, bool useCenter,
2220 const SkPaint& paint) {
2221 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2222 this->drawOval(oval, paint);
2223 } else {
2224 SkPath path;
2225 if (useCenter) {
2226 path.moveTo(oval.centerX(), oval.centerY());
2227 }
2228 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2229 if (useCenter) {
2230 path.close();
2231 }
2232 this->drawPath(path, paint);
2233 }
2234}
2235
2236void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2237 const SkPath& path, SkScalar hOffset,
2238 SkScalar vOffset, const SkPaint& paint) {
2239 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002240
reed@android.com8a1c16f2008-12-17 15:59:43 +00002241 matrix.setTranslate(hOffset, vOffset);
2242 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2243}
2244
reed@android.comf76bacf2009-05-13 14:00:33 +00002245///////////////////////////////////////////////////////////////////////////////
2246
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249}
2250
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251///////////////////////////////////////////////////////////////////////////////
2252///////////////////////////////////////////////////////////////////////////////
2253
2254SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002255 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256
2257 SkASSERT(canvas);
2258
2259 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2260 fDone = !fImpl->next();
2261}
2262
2263SkCanvas::LayerIter::~LayerIter() {
2264 fImpl->~SkDrawIter();
2265}
2266
2267void SkCanvas::LayerIter::next() {
2268 fDone = !fImpl->next();
2269}
2270
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002271SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 return fImpl->getDevice();
2273}
2274
2275const SkMatrix& SkCanvas::LayerIter::matrix() const {
2276 return fImpl->getMatrix();
2277}
2278
2279const SkPaint& SkCanvas::LayerIter::paint() const {
2280 const SkPaint* paint = fImpl->getPaint();
2281 if (NULL == paint) {
2282 paint = &fDefaultPaint;
2283 }
2284 return *paint;
2285}
2286
2287const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2288int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2289int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002290
2291///////////////////////////////////////////////////////////////////////////////
2292
2293SkCanvas::ClipVisitor::~ClipVisitor() { }