blob: 5ba6e8f6dc399c7c71fa8c858425ba826ddb6378 [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
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000586SkISize SkCanvas::getTopLayerSize() const {
587 SkBaseDevice* d = this->getTopDevice();
588 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
589}
590
591SkIPoint SkCanvas::getTopLayerOrigin() const {
592 SkBaseDevice* d = this->getTopDevice();
593 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
594}
595
596SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000597 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000598 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
599}
600
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000601SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000603 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 SkASSERT(rec && rec->fLayer);
605 return rec->fLayer->fDevice;
606}
607
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000608SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000609 if (updateMatrixClip) {
610 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
611 }
reed@google.com9266fed2011-03-30 00:18:03 +0000612 return fMCRec->fTopLayer->fDevice;
613}
614
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000615SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000617 SkDeque::F2BIter iter(fMCStack);
618 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000620 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621
622 if (rootDevice == device) {
623 return device;
624 }
reed@google.com4b226022011-01-11 18:32:13 +0000625
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000627 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 }
629 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000630 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 }
632
633 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
634 rootDevice = device;
635
636 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000637
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 /* Now we update our initial region to have the bounds of the new device,
639 and then intersect all of the clips in our stack with these bounds,
640 to ensure that we can't draw outside of the device's bounds (and trash
641 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 NOTE: this is only a partial-fix, since if the new device is larger than
644 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000645 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
647 reconstruct the correct clips, so this approximation will have to do.
648 The caller really needs to restore() back to the base if they want to
649 accurately take advantage of the new device bounds.
650 */
651
reed@google.com42aea282012-03-28 16:19:15 +0000652 SkIRect bounds;
653 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000655 } else {
656 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 }
reed@google.com42aea282012-03-28 16:19:15 +0000658 // now jam our 1st clip to be bounds, and intersect the rest with that
659 rec->fRasterClip->setRect(bounds);
660 while ((rec = (MCRec*)iter.next()) != NULL) {
661 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
662 }
663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 return device;
665}
666
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000667bool SkCanvas::readPixels(SkBitmap* bitmap,
668 int x, int y,
669 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000670 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000671 if (!device) {
672 return false;
673 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000674 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000675}
676
bsalomon@google.comc6980972011-11-02 19:57:21 +0000677bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000678 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000679 if (!device) {
680 return false;
681 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000682
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000683 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000684 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000685 if (!bounds.intersect(srcRect)) {
686 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000687 }
688
689 SkBitmap tmp;
690 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
691 bounds.height());
692 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
693 bitmap->swap(tmp);
694 return true;
695 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000696 return false;
697 }
reed@google.com51df9e32010-12-23 19:29:18 +0000698}
699
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000700void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
701 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000702 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000703 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000704 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
705 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
706 device->accessBitmap(true);
707 device->writePixels(bitmap, x, y, config8888);
708 }
reed@google.com51df9e32010-12-23 19:29:18 +0000709 }
710}
711
junov@google.com4370aed2012-01-18 16:21:08 +0000712SkCanvas* SkCanvas::canvasForDrawIter() {
713 return this;
714}
715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716//////////////////////////////////////////////////////////////////////////////
717
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718void SkCanvas::updateDeviceCMCache() {
719 if (fDeviceCMDirty) {
720 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000721 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000723
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000725 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000727 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000729 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 } while ((layer = layer->fNext) != NULL);
731 }
732 fDeviceCMDirty = false;
733 }
734}
735
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736///////////////////////////////////////////////////////////////////////////////
737
738int SkCanvas::internalSave(SaveFlags flags) {
739 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000740
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 MCRec* newTop = (MCRec*)fMCStack.push_back();
742 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000743
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 newTop->fNext = fMCRec;
745 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000746
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000747 if (SkCanvas::kClip_SaveFlag & flags) {
748 fClipStack.save();
749 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000750
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 return saveCount;
752}
753
754int SkCanvas::save(SaveFlags flags) {
755 // call shared impl
756 return this->internalSave(flags);
757}
758
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
760 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
761}
762
junov@chromium.orga907ac32012-02-24 21:54:07 +0000763bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000764 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000765 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000766 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000767 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000768 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000769 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000770
771 if (imageFilter) {
772 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
773 // Filters may grow the bounds beyond the device bounds.
774 op = SkRegion::kReplace_Op;
775 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000776 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 if (NULL != bounds) {
778 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 this->getTotalMatrix().mapRect(&r, *bounds);
781 r.roundOut(&ir);
782 // early exit if the layer's bounds are clipped out
783 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000784 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000785 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000786 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000787 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 }
789 } else { // no user bounds, so just use the clip
790 ir = clipBounds;
791 }
792
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000793 if (bounds_affects_clip(flags)) {
794 fClipStack.clipDevRect(ir, op);
795 // early exit if the clip is now empty
796 if (!fMCRec->fRasterClip->op(ir, op)) {
797 return false;
798 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000799 }
800
801 if (intersection) {
802 *intersection = ir;
803 }
804 return true;
805}
806
807int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
808 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000809 return this->internalSaveLayer(bounds, paint, flags, false);
810}
811
reed@google.com76f10a32014-02-05 15:32:21 +0000812static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000813 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000814 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000815 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000816}
817
reed@google.com8926b162012-03-23 15:36:36 +0000818int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
819 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000820 // do this before we create the layer. We don't call the public save() since
821 // that would invoke a possibly overridden virtual
822 int count = this->internalSave(flags);
823
824 fDeviceCMDirty = true;
825
826 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000827 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 return count;
829 }
830
reed@google.comb55deeb2012-01-06 14:43:09 +0000831 // Kill the imagefilter if our device doesn't allow it
832 SkLazyPaint lazyP;
833 if (paint && paint->getImageFilter()) {
834 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000835 if (justForImageFilter) {
836 // early exit if the layer was just for the imageFilter
837 return count;
838 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000839 SkPaint* p = lazyP.set(*paint);
840 p->setImageFilter(NULL);
841 paint = p;
842 }
843 }
844
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000845 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
846 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
847 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000849 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000850 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000851 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000852 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000853 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000854 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000855 if (NULL == device) {
856 SkDebugf("Unable to create device for layer.");
857 return count;
858 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000859
reed@google.com6f8f2922011-03-04 22:27:10 +0000860 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000861 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 device->unref();
863
864 layer->fNext = fMCRec->fTopLayer;
865 fMCRec->fLayer = layer;
866 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
867
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000868 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 return count;
870}
871
872int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
873 SaveFlags flags) {
874 if (0xFF == alpha) {
875 return this->saveLayer(bounds, NULL, flags);
876 } else {
877 SkPaint tmpPaint;
878 tmpPaint.setAlpha(alpha);
879 return this->saveLayer(bounds, &tmpPaint, flags);
880 }
881}
882
883void SkCanvas::restore() {
884 // check for underflow
885 if (fMCStack.count() > 1) {
886 this->internalRestore();
887 }
888}
889
890void SkCanvas::internalRestore() {
891 SkASSERT(fMCStack.count() != 0);
892
893 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000894 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000896 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
897 fClipStack.restore();
898 }
899
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000900 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 DeviceCM* layer = fMCRec->fLayer; // may be null
902 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
903 fMCRec->fLayer = NULL;
904
905 // now do the normal restore()
906 fMCRec->~MCRec(); // balanced in save()
907 fMCStack.pop_back();
908 fMCRec = (MCRec*)fMCStack.back();
909
910 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
911 since if we're being recorded, we don't want to record this (the
912 recorder will have already recorded the restore).
913 */
914 if (NULL != layer) {
915 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000916 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000917 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
918 layer->fPaint);
919 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000921
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000922 SkASSERT(fSaveLayerCount > 0);
923 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 }
925 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000926 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927}
928
929int SkCanvas::getSaveCount() const {
930 return fMCStack.count();
931}
932
933void SkCanvas::restoreToCount(int count) {
934 // sanity check
935 if (count < 1) {
936 count = 1;
937 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000938
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000939 int n = this->getSaveCount() - count;
940 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 this->restore();
942 }
943}
944
reed@google.com7c202932011-12-14 18:48:05 +0000945bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000946 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000947}
948
reed@google.com76f10a32014-02-05 15:32:21 +0000949SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
950 return this->onNewSurface(info);
951}
952
953SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
954 SkBaseDevice* dev = this->getDevice();
955 return dev ? dev->newSurface(info) : NULL;
956}
957
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000958SkImageInfo SkCanvas::imageInfo() const {
959 SkBaseDevice* dev = this->getDevice();
960 if (dev) {
961 return dev->imageInfo();
962 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000963 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000964 }
965}
966
967const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
968 return this->onPeekPixels(info, rowBytes);
969}
970
971const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
972 SkBaseDevice* dev = this->getDevice();
973 return dev ? dev->peekPixels(info, rowBytes) : NULL;
974}
975
976SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
977 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
978 if (NULL == fAddr) {
979 fInfo = canvas->imageInfo();
980 if (kUnknown_SkColorType == fInfo.colorType() ||
981 !fBitmap.allocPixels(fInfo))
982 {
983 return; // failure, fAddr is NULL
984 }
985 fBitmap.lockPixels();
986 if (!canvas->readPixels(&fBitmap, 0, 0)) {
987 return; // failure, fAddr is NULL
988 }
989 fAddr = fBitmap.getPixels();
990 fRowBytes = fBitmap.rowBytes();
991 }
992 SkASSERT(fAddr); // success
993}
994
995bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
996 if (fAddr) {
997 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
998 NULL, NULL);
999 } else {
1000 bitmap->reset();
1001 return false;
1002 }
1003}
1004
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005/////////////////////////////////////////////////////////////////////////////
1006
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001007void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001009 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 return;
1011 }
1012
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001013 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001015 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001017
1018 SkDEBUGCODE(bitmap.validate();)
1019 CHECK_LOCKCOUNT_BALANCE(bitmap);
1020
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001021 SkRect storage;
1022 const SkRect* bounds = NULL;
1023 if (paint && paint->canComputeFastBounds()) {
1024 bitmap.getBounds(&storage);
1025 matrix.mapRect(&storage);
1026 bounds = &paint->computeFastBounds(storage, &storage);
1027 }
1028
1029 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001030
1031 while (iter.next()) {
1032 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1033 }
1034
1035 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036}
1037
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001038void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001039 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 SkPaint tmp;
1041 if (NULL == paint) {
1042 tmp.setDither(true);
1043 paint = &tmp;
1044 }
reed@google.com4b226022011-01-11 18:32:13 +00001045
reed@google.com8926b162012-03-23 15:36:36 +00001046 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001048 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001049 paint = &looper.paint();
1050 SkImageFilter* filter = paint->getImageFilter();
1051 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001052 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001053 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001054 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001055 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001056 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001057 SkMatrix matrix = *iter.fMatrix;
1058 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001059 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001060 SkPaint tmpUnfiltered(*paint);
1061 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001062 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1063 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001064 }
1065 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001066 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001067 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001069 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070}
1071
reed@google.com8926b162012-03-23 15:36:36 +00001072void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1073 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001074 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001075 return;
1076 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001077 SkDEBUGCODE(bitmap.validate();)
1078 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001079
reed@google.com8926b162012-03-23 15:36:36 +00001080 SkPaint tmp;
1081 if (NULL == paint) {
1082 paint = &tmp;
1083 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001084
reed@google.com8926b162012-03-23 15:36:36 +00001085 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001086
reed@google.com8926b162012-03-23 15:36:36 +00001087 while (iter.next()) {
1088 paint = &looper.paint();
1089 SkImageFilter* filter = paint->getImageFilter();
1090 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1091 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001092 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001093 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001094 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001095 SkMatrix matrix = *iter.fMatrix;
1096 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001097 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001098 SkPaint tmpUnfiltered(*paint);
1099 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001100 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001101 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001102 }
1103 } else {
1104 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1105 }
1106 }
1107 LOOPER_END
1108}
1109
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110/////////////////////////////////////////////////////////////////////////////
1111
1112bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1113 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001114 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 return fMCRec->fMatrix->preTranslate(dx, dy);
1116}
1117
1118bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1119 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001120 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 return fMCRec->fMatrix->preScale(sx, sy);
1122}
1123
1124bool SkCanvas::rotate(SkScalar degrees) {
1125 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001126 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 return fMCRec->fMatrix->preRotate(degrees);
1128}
1129
1130bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1131 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001132 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 return fMCRec->fMatrix->preSkew(sx, sy);
1134}
1135
1136bool SkCanvas::concat(const SkMatrix& matrix) {
1137 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001138 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 return fMCRec->fMatrix->preConcat(matrix);
1140}
1141
1142void SkCanvas::setMatrix(const SkMatrix& matrix) {
1143 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001144 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 *fMCRec->fMatrix = matrix;
1146}
1147
1148// this is not virtual, so it must call a virtual method so that subclasses
1149// will see its action
1150void SkCanvas::resetMatrix() {
1151 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001152
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 matrix.reset();
1154 this->setMatrix(matrix);
1155}
1156
1157//////////////////////////////////////////////////////////////////////////////
1158
reed@google.comc42d35d2011-10-12 11:57:42 +00001159bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001160#ifdef SK_ENABLE_CLIP_QUICKREJECT
1161 if (SkRegion::kIntersect_Op == op) {
1162 if (fMCRec->fRasterClip->isEmpty()) {
1163 return false;
1164 }
1165
reed@google.com3b3e8952012-08-16 20:53:31 +00001166 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001167 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001168 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001169
1170 fClipStack.clipEmpty();
1171 return fMCRec->fRasterClip->setEmpty();
1172 }
1173 }
1174#endif
1175
reed@google.com5c3d1472011-02-22 19:12:23 +00001176 AutoValidateClip avc(this);
1177
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001179 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001180 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181
1182 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001183 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001184 // the matrix. This means we don't have to a) make a path, and b) tell
1185 // the region code to scan-convert the path, only to discover that it
1186 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188
1189 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001190 fClipStack.clipDevRect(r, op, doAA);
1191 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001193 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001194 // and clip against that, since it can handle any matrix. However, to
1195 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1196 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 SkPath path;
1198
1199 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001200 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 }
1202}
1203
reed@google.com00177082011-10-12 14:34:30 +00001204static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001205 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001206 // base is used to limit the size (and therefore memory allocation) of the
1207 // region that results from scan converting devPath.
1208 SkRegion base;
1209
reed@google.com819c9212011-02-23 18:56:55 +00001210 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001211 // since we are intersect, we can do better (tighter) with currRgn's
1212 // bounds, than just using the device. However, if currRgn is complex,
1213 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001214 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001215 // FIXME: we should also be able to do this when currClip->isBW(),
1216 // but relaxing the test above triggers GM asserts in
1217 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1218 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001219 } else {
reed@google.com00177082011-10-12 14:34:30 +00001220 base.setRect(currClip->getBounds());
1221 SkRasterClip clip;
1222 clip.setPath(devPath, base, doAA);
1223 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001224 }
reed@google.com819c9212011-02-23 18:56:55 +00001225 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001226 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001227 if (!device) {
1228 return currClip->setEmpty();
1229 }
1230
junov@chromium.orga907ac32012-02-24 21:54:07 +00001231 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001232
1233 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001234 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001235 } else {
reed@google.com00177082011-10-12 14:34:30 +00001236 SkRasterClip clip;
1237 clip.setPath(devPath, base, doAA);
1238 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001239 }
1240 }
1241}
1242
reed@google.com4ed0fb72012-12-12 20:48:18 +00001243bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1244 if (rrect.isRect()) {
1245 // call the non-virtual version
1246 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001247 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001248
1249 SkRRect transformedRRect;
1250 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1251 AutoValidateClip avc(this);
1252
1253 fDeviceCMDirty = true;
1254 fCachedLocalClipBoundsDirty = true;
1255 doAA &= fAllowSoftClip;
1256
1257 fClipStack.clipDevRRect(transformedRRect, op, doAA);
1258
1259 SkPath devPath;
1260 devPath.addRRect(transformedRRect);
1261
1262 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
1263 }
1264
1265 SkPath path;
1266 path.addRRect(rrect);
1267 // call the non-virtual version
1268 return this->SkCanvas::clipPath(path, op, doAA);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001269}
1270
reed@google.comc42d35d2011-10-12 11:57:42 +00001271bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001272#ifdef SK_ENABLE_CLIP_QUICKREJECT
1273 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1274 if (fMCRec->fRasterClip->isEmpty()) {
1275 return false;
1276 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001277
reed@google.com3b3e8952012-08-16 20:53:31 +00001278 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001279 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001280 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001281
reed@google.comda17f752012-08-16 18:27:05 +00001282 fClipStack.clipEmpty();
1283 return fMCRec->fRasterClip->setEmpty();
1284 }
1285 }
1286#endif
1287
reed@google.com5c3d1472011-02-22 19:12:23 +00001288 AutoValidateClip avc(this);
1289
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001291 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001292 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293
1294 SkPath devPath;
1295 path.transform(*fMCRec->fMatrix, &devPath);
1296
reed@google.comfe701122011-11-08 19:41:23 +00001297 // Check if the transfomation, or the original path itself
1298 // made us empty. Note this can also happen if we contained NaN
1299 // values. computing the bounds detects this, and will set our
1300 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1301 if (devPath.getBounds().isEmpty()) {
1302 // resetting the path will remove any NaN or other wanky values
1303 // that might upset our scan converter.
1304 devPath.reset();
1305 }
1306
reed@google.com5c3d1472011-02-22 19:12:23 +00001307 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001308 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001309
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001310 if (fAllowSimplifyClip) {
1311 devPath.reset();
1312 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1313 const SkClipStack* clipStack = getClipStack();
1314 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1315 const SkClipStack::Element* element;
1316 while ((element = iter.next())) {
1317 SkClipStack::Element::Type type = element->getType();
1318 if (type == SkClipStack::Element::kEmpty_Type) {
1319 continue;
1320 }
1321 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001322 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001323 SkRegion::Op elementOp = element->getOp();
1324 if (elementOp == SkRegion::kReplace_Op) {
1325 devPath = operand;
1326 } else {
1327 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1328 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001329 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1330 // perhaps we need an API change to avoid this sort of mixed-signals about
1331 // clipping.
1332 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001333 }
1334 op = SkRegion::kReplace_Op;
1335 }
1336
reed@google.com00177082011-10-12 14:34:30 +00001337 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338}
1339
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001340bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1341 bool inverseFilled) {
1342 // This is for updating the clip conservatively using only bounds
1343 // information.
1344 // Contract:
1345 // The current clip must contain the true clip. The true
1346 // clip is the clip that would have normally been computed
1347 // by calls to clipPath and clipRRect
1348 // Objective:
1349 // Keep the current clip as small as possible without
1350 // breaking the contract, using only clip bounding rectangles
1351 // (for performance).
1352
1353 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1354 // don't have to worry about getting caught in a loop. Thus anywhere
1355 // we call a virtual method, we explicitly prefix it with
1356 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001357
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001358 if (inverseFilled) {
1359 switch (op) {
1360 case SkRegion::kIntersect_Op:
1361 case SkRegion::kDifference_Op:
1362 // These ops can only shrink the current clip. So leaving
1363 // the clip unchanges conservatively respects the contract.
1364 return this->getClipDeviceBounds(NULL);
1365 case SkRegion::kUnion_Op:
1366 case SkRegion::kReplace_Op:
1367 case SkRegion::kReverseDifference_Op:
1368 case SkRegion::kXOR_Op:
1369 {
1370 // These ops can grow the current clip up to the extents of
1371 // the input clip, which is inverse filled, so we just set
1372 // the current clip to the device bounds.
1373 SkRect deviceBounds;
1374 SkIRect deviceIBounds;
1375 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001376 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001377 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1378 // set the clip in device space
1379 this->SkCanvas::setMatrix(SkMatrix::I());
1380 bool result = this->SkCanvas::clipRect(deviceBounds,
1381 SkRegion::kReplace_Op, false);
1382 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1383 return result;
1384 }
1385 default:
1386 SkASSERT(0); // unhandled op?
1387 }
1388 } else {
1389 // Not inverse filled
1390 switch (op) {
1391 case SkRegion::kIntersect_Op:
1392 case SkRegion::kUnion_Op:
1393 case SkRegion::kReplace_Op:
1394 return this->SkCanvas::clipRect(bounds, op, false);
1395 case SkRegion::kDifference_Op:
1396 // Difference can only shrink the current clip.
1397 // Leaving clip unchanged conservatively fullfills the contract.
1398 return this->getClipDeviceBounds(NULL);
1399 case SkRegion::kReverseDifference_Op:
1400 // To reverse, we swap in the bounds with a replace op.
1401 // As with difference, leave it unchanged.
1402 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1403 case SkRegion::kXOR_Op:
1404 // Be conservative, based on (A XOR B) always included in (A union B),
1405 // which is always included in (bounds(A) union bounds(B))
1406 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1407 default:
1408 SkASSERT(0); // unhandled op?
1409 }
1410 }
1411 return true;
1412}
1413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001415 AutoValidateClip avc(this);
1416
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001418 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419
reed@google.com5c3d1472011-02-22 19:12:23 +00001420 // todo: signal fClipStack that we have a region, and therefore (I guess)
1421 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001422 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001423
reed@google.com00177082011-10-12 14:34:30 +00001424 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425}
1426
reed@google.com819c9212011-02-23 18:56:55 +00001427#ifdef SK_DEBUG
1428void SkCanvas::validateClip() const {
1429 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001430 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001431 if (!device) {
1432 SkASSERT(this->getTotalClip().isEmpty());
1433 return;
1434 }
1435
reed@google.com819c9212011-02-23 18:56:55 +00001436 SkIRect ir;
1437 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001438 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001439
robertphillips@google.com80214e22012-07-20 15:33:18 +00001440 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001441 const SkClipStack::Element* element;
1442 while ((element = iter.next()) != NULL) {
1443 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001444 case SkClipStack::Element::kRect_Type:
1445 element->getRect().round(&ir);
1446 tmpClip.op(ir, element->getOp());
1447 break;
1448 case SkClipStack::Element::kEmpty_Type:
1449 tmpClip.setEmpty();
1450 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001451 default: {
1452 SkPath path;
1453 element->asPath(&path);
1454 clipPathHelper(this,
1455 &tmpClip,
1456 path,
1457 element->getOp(),
1458 element->isAA());
1459 break;
1460 }
reed@google.com819c9212011-02-23 18:56:55 +00001461 }
1462 }
1463
reed@google.com6f8f2922011-03-04 22:27:10 +00001464#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001465 // now compare against the current rgn
1466 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001467 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001468#endif
reed@google.com819c9212011-02-23 18:56:55 +00001469}
1470#endif
1471
reed@google.com90c07ea2012-04-13 13:50:27 +00001472void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001473 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001474 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001475
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001476 static const SkRect kEmpty = { 0, 0, 0, 0 };
1477 while ((element = iter.next()) != NULL) {
1478 switch (element->getType()) {
1479 case SkClipStack::Element::kPath_Type:
1480 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1481 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001482 case SkClipStack::Element::kRRect_Type:
1483 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1484 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001485 case SkClipStack::Element::kRect_Type:
1486 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1487 break;
1488 case SkClipStack::Element::kEmpty_Type:
1489 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1490 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001491 }
1492 }
1493}
1494
reed@google.com5c3d1472011-02-22 19:12:23 +00001495///////////////////////////////////////////////////////////////////////////////
1496
reed@google.com754de5f2014-02-24 19:38:20 +00001497bool SkCanvas::isClipEmpty() const {
1498 return fMCRec->fRasterClip->isEmpty();
1499}
1500
reed@google.com3b3e8952012-08-16 20:53:31 +00001501bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001502
reed@google.com16078632011-12-06 18:56:37 +00001503 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001504 return true;
1505
reed@google.com00177082011-10-12 14:34:30 +00001506 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507 return true;
1508 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509
tomhudson@google.com8d430182011-06-06 19:11:19 +00001510 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001511 SkRect dst;
1512 fMCRec->fMatrix->mapRect(&dst, rect);
1513 SkIRect idst;
1514 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001515 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001516 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001517 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001518
reed@android.coma380ae42009-07-21 01:17:02 +00001519 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001520 // TODO: should we use | instead, or compare all 4 at once?
1521 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001522 return true;
1523 }
reed@google.comc0784db2013-12-13 21:16:12 +00001524 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001525 return true;
1526 }
1527 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529}
1530
reed@google.com3b3e8952012-08-16 20:53:31 +00001531bool SkCanvas::quickReject(const SkPath& path) const {
1532 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533}
1534
reed@google.com3b3e8952012-08-16 20:53:31 +00001535bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001536 SkIRect ibounds;
1537 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 return false;
1539 }
1540
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001541 SkMatrix inverse;
1542 // if we can't invert the CTM, we can't return local clip bounds
1543 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001544 if (bounds) {
1545 bounds->setEmpty();
1546 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001547 return false;
1548 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001550 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001551 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001552 // adjust it outwards in case we are antialiasing
1553 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001554
reed@google.com8f4d2302013-12-17 16:44:46 +00001555 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1556 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557 inverse.mapRect(bounds, r);
1558 }
1559 return true;
1560}
1561
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001562bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001563 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001564 if (clip.isEmpty()) {
1565 if (bounds) {
1566 bounds->setEmpty();
1567 }
1568 return false;
1569 }
1570
1571 if (NULL != bounds) {
1572 *bounds = clip.getBounds();
1573 }
1574 return true;
1575}
1576
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577const SkMatrix& SkCanvas::getTotalMatrix() const {
1578 return *fMCRec->fMatrix;
1579}
1580
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001581SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001582 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1583 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001584 return kComplex_ClipType;
1585}
1586
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001588 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589}
1590
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001591SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001592 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001593 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594}
1595
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001596GrContext* SkCanvas::getGrContext() {
1597#if SK_SUPPORT_GPU
1598 SkBaseDevice* device = this->getTopDevice();
1599 if (NULL != device) {
1600 GrRenderTarget* renderTarget = device->accessRenderTarget();
1601 if (NULL != renderTarget) {
1602 return renderTarget->getContext();
1603 }
1604 }
1605#endif
1606
1607 return NULL;
1608
1609}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001610
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001611void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1612 const SkPaint& paint) {
1613 if (outer.isEmpty()) {
1614 return;
1615 }
1616 if (inner.isEmpty()) {
1617 this->drawRRect(outer, paint);
1618 return;
1619 }
1620
1621 // We don't have this method (yet), but technically this is what we should
1622 // be able to assert...
1623 // SkASSERT(outer.contains(inner));
1624 //
1625 // For now at least check for containment of bounds
1626 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1627
1628 this->onDrawDRRect(outer, inner, paint);
1629}
1630
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631//////////////////////////////////////////////////////////////////////////////
1632// These are the virtual drawing methods
1633//////////////////////////////////////////////////////////////////////////////
1634
reed@google.com2a981812011-04-14 18:59:28 +00001635void SkCanvas::clear(SkColor color) {
1636 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001637 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001638 while (iter.next()) {
1639 iter.fDevice->clear(color);
1640 }
1641}
1642
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001644 this->internalDrawPaint(paint);
1645}
1646
1647void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001648 CHECK_SHADER_NOSETCONTEXT(paint);
1649
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001650 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651
1652 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001653 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654 }
1655
reed@google.com4e2b3d32011-04-07 14:18:59 +00001656 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657}
1658
1659void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1660 const SkPaint& paint) {
1661 if ((long)count <= 0) {
1662 return;
1663 }
1664
reed@google.comea033602012-12-14 13:13:55 +00001665 CHECK_SHADER_NOSETCONTEXT(paint);
1666
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001667 SkRect r, storage;
1668 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001669 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001670 // special-case 2 points (common for drawing a single line)
1671 if (2 == count) {
1672 r.set(pts[0], pts[1]);
1673 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001674 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001675 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001676 bounds = &paint.computeFastStrokeBounds(r, &storage);
1677 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001678 return;
1679 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001680 }
reed@google.coma584aed2012-05-16 14:06:02 +00001681
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682 SkASSERT(pts != NULL);
1683
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001684 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001685
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001687 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688 }
reed@google.com4b226022011-01-11 18:32:13 +00001689
reed@google.com4e2b3d32011-04-07 14:18:59 +00001690 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691}
1692
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001693void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001694 CHECK_SHADER_NOSETCONTEXT(paint);
1695
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001696 SkRect storage;
1697 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001698 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001699 bounds = &paint.computeFastBounds(r, &storage);
1700 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701 return;
1702 }
1703 }
reed@google.com4b226022011-01-11 18:32:13 +00001704
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001705 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706
1707 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001708 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709 }
1710
reed@google.com4e2b3d32011-04-07 14:18:59 +00001711 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712}
1713
reed@google.com4ed0fb72012-12-12 20:48:18 +00001714void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001715 CHECK_SHADER_NOSETCONTEXT(paint);
1716
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001717 SkRect storage;
1718 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001719 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001720 bounds = &paint.computeFastBounds(oval, &storage);
1721 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001722 return;
1723 }
1724 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001725
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001726 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001727
1728 while (iter.next()) {
1729 iter.fDevice->drawOval(iter, oval, looper.paint());
1730 }
1731
1732 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001733}
1734
1735void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001736 CHECK_SHADER_NOSETCONTEXT(paint);
1737
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001738 SkRect storage;
1739 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001740 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001741 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1742 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001743 return;
1744 }
1745 }
1746
1747 if (rrect.isRect()) {
1748 // call the non-virtual version
1749 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001750 return;
1751 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001752 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001753 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1754 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001755 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001756
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001757 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001758
1759 while (iter.next()) {
1760 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1761 }
1762
1763 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001764}
1765
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001766void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1767 const SkPaint& paint) {
1768 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001769
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001770 SkRect storage;
1771 const SkRect* bounds = NULL;
1772 if (paint.canComputeFastBounds()) {
1773 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1774 if (this->quickReject(*bounds)) {
1775 return;
1776 }
1777 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001778
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001779 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001780
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001781 while (iter.next()) {
1782 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1783 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001784
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001785 LOOPER_END
1786}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001787
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001788void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001789 CHECK_SHADER_NOSETCONTEXT(paint);
1790
reed@google.com93645112012-07-26 16:11:47 +00001791 if (!path.isFinite()) {
1792 return;
1793 }
1794
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001795 SkRect storage;
1796 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001797 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001798 const SkRect& pathBounds = path.getBounds();
1799 bounds = &paint.computeFastBounds(pathBounds, &storage);
1800 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801 return;
1802 }
1803 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001804
1805 const SkRect& r = path.getBounds();
1806 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001807 if (path.isInverseFillType()) {
1808 this->internalDrawPaint(paint);
1809 }
1810 return;
1811 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001813 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814
1815 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001816 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 }
1818
reed@google.com4e2b3d32011-04-07 14:18:59 +00001819 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001820}
1821
1822void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1823 const SkPaint* paint) {
1824 SkDEBUGCODE(bitmap.validate();)
1825
reed@google.com3d608122011-11-21 15:16:16 +00001826 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001827 SkRect bounds = {
1828 x, y,
1829 x + SkIntToScalar(bitmap.width()),
1830 y + SkIntToScalar(bitmap.height())
1831 };
1832 if (paint) {
1833 (void)paint->computeFastBounds(bounds, &bounds);
1834 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001835 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836 return;
1837 }
1838 }
reed@google.com4b226022011-01-11 18:32:13 +00001839
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840 SkMatrix matrix;
1841 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001842 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843}
1844
reed@google.com9987ec32011-09-07 11:57:52 +00001845// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001846void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001847 const SkRect& dst, const SkPaint* paint,
1848 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001849 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001850 return;
1851 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001852
reed@google.comea033602012-12-14 13:13:55 +00001853 CHECK_LOCKCOUNT_BALANCE(bitmap);
1854
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001855 SkRect storage;
1856 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001857 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001858 if (paint) {
1859 bounds = &paint->computeFastBounds(dst, &storage);
1860 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001861 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001862 return;
1863 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864 }
reed@google.com3d608122011-11-21 15:16:16 +00001865
reed@google.com33535f32012-09-25 15:37:50 +00001866 SkLazyPaint lazy;
1867 if (NULL == paint) {
1868 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001870
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001871 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001872
reed@google.com33535f32012-09-25 15:37:50 +00001873 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001874 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001875 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001876
reed@google.com33535f32012-09-25 15:37:50 +00001877 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878}
1879
reed@google.com71121732012-09-18 15:14:33 +00001880void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001881 const SkRect& dst, const SkPaint* paint,
1882 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001883 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001884 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001885}
1886
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1888 const SkPaint* paint) {
1889 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001890 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891}
1892
reed@google.com9987ec32011-09-07 11:57:52 +00001893void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1894 const SkIRect& center, const SkRect& dst,
1895 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001896 if (bitmap.drawsNothing()) {
1897 return;
1898 }
reed@google.com3d608122011-11-21 15:16:16 +00001899 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001900 SkRect storage;
1901 const SkRect* bounds = &dst;
1902 if (paint) {
1903 bounds = &paint->computeFastBounds(dst, &storage);
1904 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001905 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001906 return;
1907 }
1908 }
1909
reed@google.com9987ec32011-09-07 11:57:52 +00001910 const int32_t w = bitmap.width();
1911 const int32_t h = bitmap.height();
1912
1913 SkIRect c = center;
1914 // pin center to the bounds of the bitmap
1915 c.fLeft = SkMax32(0, center.fLeft);
1916 c.fTop = SkMax32(0, center.fTop);
1917 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1918 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1919
reed@google.com71121732012-09-18 15:14:33 +00001920 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001921 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001922 };
1923 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001924 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001925 };
reed@google.com9987ec32011-09-07 11:57:52 +00001926 SkScalar dstX[4] = {
1927 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1928 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1929 };
1930 SkScalar dstY[4] = {
1931 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1932 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1933 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001934
reed@google.com9987ec32011-09-07 11:57:52 +00001935 if (dstX[1] > dstX[2]) {
1936 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1937 dstX[2] = dstX[1];
1938 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001939
reed@google.com9987ec32011-09-07 11:57:52 +00001940 if (dstY[1] > dstY[2]) {
1941 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1942 dstY[2] = dstY[1];
1943 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001944
reed@google.com9987ec32011-09-07 11:57:52 +00001945 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001946 SkRect s, d;
1947
reed@google.com9987ec32011-09-07 11:57:52 +00001948 s.fTop = srcY[y];
1949 s.fBottom = srcY[y+1];
1950 d.fTop = dstY[y];
1951 d.fBottom = dstY[y+1];
1952 for (int x = 0; x < 3; x++) {
1953 s.fLeft = srcX[x];
1954 s.fRight = srcX[x+1];
1955 d.fLeft = dstX[x];
1956 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001957 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001958 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001959 }
1960 }
1961}
1962
1963void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1964 const SkRect& dst, const SkPaint* paint) {
1965 SkDEBUGCODE(bitmap.validate();)
1966
1967 // Need a device entry-point, so gpu can use a mesh
1968 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1969}
1970
reed@google.comf67e4cf2011-03-15 20:56:58 +00001971class SkDeviceFilteredPaint {
1972public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001973 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1974 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001975 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001976 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001977 newPaint->setFlags(flags.fFlags);
1978 newPaint->setHinting(flags.fHinting);
1979 fPaint = newPaint;
1980 } else {
1981 fPaint = &paint;
1982 }
1983 }
1984
reed@google.comf67e4cf2011-03-15 20:56:58 +00001985 const SkPaint& paint() const { return *fPaint; }
1986
1987private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001988 const SkPaint* fPaint;
1989 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001990};
1991
bungeman@google.com52c748b2011-08-22 21:30:43 +00001992void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1993 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001994 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001995 draw.fDevice->drawRect(draw, r, paint);
1996 } else {
1997 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001998 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001999 draw.fDevice->drawRect(draw, r, p);
2000 }
2001}
2002
2003void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2004 const char text[], size_t byteLength,
2005 SkScalar x, SkScalar y) {
2006 SkASSERT(byteLength == 0 || text != NULL);
2007
2008 // nothing to draw
2009 if (text == NULL || byteLength == 0 ||
2010 draw.fClip->isEmpty() ||
2011 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2012 return;
2013 }
2014
2015 SkScalar width = 0;
2016 SkPoint start;
2017
2018 start.set(0, 0); // to avoid warning
2019 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2020 SkPaint::kStrikeThruText_Flag)) {
2021 width = paint.measureText(text, byteLength);
2022
2023 SkScalar offsetX = 0;
2024 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2025 offsetX = SkScalarHalf(width);
2026 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2027 offsetX = width;
2028 }
2029 start.set(x - offsetX, y);
2030 }
2031
2032 if (0 == width) {
2033 return;
2034 }
2035
2036 uint32_t flags = paint.getFlags();
2037
2038 if (flags & (SkPaint::kUnderlineText_Flag |
2039 SkPaint::kStrikeThruText_Flag)) {
2040 SkScalar textSize = paint.getTextSize();
2041 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2042 SkRect r;
2043
2044 r.fLeft = start.fX;
2045 r.fRight = start.fX + width;
2046
2047 if (flags & SkPaint::kUnderlineText_Flag) {
2048 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2049 start.fY);
2050 r.fTop = offset;
2051 r.fBottom = offset + height;
2052 DrawRect(draw, paint, r, textSize);
2053 }
2054 if (flags & SkPaint::kStrikeThruText_Flag) {
2055 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2056 start.fY);
2057 r.fTop = offset;
2058 r.fBottom = offset + height;
2059 DrawRect(draw, paint, r, textSize);
2060 }
2061 }
2062}
2063
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064void SkCanvas::drawText(const void* text, size_t byteLength,
2065 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002066 CHECK_SHADER_NOSETCONTEXT(paint);
2067
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002068 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069
2070 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002071 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002072 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002073 DrawTextDecorations(iter, dfp.paint(),
2074 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 }
2076
reed@google.com4e2b3d32011-04-07 14:18:59 +00002077 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078}
2079
2080void SkCanvas::drawPosText(const void* text, size_t byteLength,
2081 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002082 CHECK_SHADER_NOSETCONTEXT(paint);
2083
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002084 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002085
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002087 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002089 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 }
reed@google.com4b226022011-01-11 18:32:13 +00002091
reed@google.com4e2b3d32011-04-07 14:18:59 +00002092 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002093}
2094
2095void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2096 const SkScalar xpos[], SkScalar constY,
2097 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002098 CHECK_SHADER_NOSETCONTEXT(paint);
2099
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002100 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002101
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002103 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002105 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106 }
reed@google.com4b226022011-01-11 18:32:13 +00002107
reed@google.com4e2b3d32011-04-07 14:18:59 +00002108 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109}
2110
2111void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2112 const SkPath& path, const SkMatrix* matrix,
2113 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002114 CHECK_SHADER_NOSETCONTEXT(paint);
2115
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002116 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117
2118 while (iter.next()) {
2119 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002120 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002121 }
2122
reed@google.com4e2b3d32011-04-07 14:18:59 +00002123 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124}
2125
2126void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2127 const SkPoint verts[], const SkPoint texs[],
2128 const SkColor colors[], SkXfermode* xmode,
2129 const uint16_t indices[], int indexCount,
2130 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002131 CHECK_SHADER_NOSETCONTEXT(paint);
2132
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002133 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002134
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135 while (iter.next()) {
2136 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002137 colors, xmode, indices, indexCount,
2138 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139 }
reed@google.com4b226022011-01-11 18:32:13 +00002140
reed@google.com4e2b3d32011-04-07 14:18:59 +00002141 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142}
2143
2144//////////////////////////////////////////////////////////////////////////////
2145// These methods are NOT virtual, and therefore must call back into virtual
2146// methods, rather than actually drawing themselves.
2147//////////////////////////////////////////////////////////////////////////////
2148
2149void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002150 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151 SkPaint paint;
2152
2153 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002154 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002155 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156 }
2157 this->drawPaint(paint);
2158}
2159
reed@android.com845fdac2009-06-23 03:01:32 +00002160void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161 SkPaint paint;
2162
2163 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002164 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002165 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166 }
2167 this->drawPaint(paint);
2168}
2169
2170void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2171 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002172
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 pt.set(x, y);
2174 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2175}
2176
2177void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2178 SkPoint pt;
2179 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002180
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 pt.set(x, y);
2182 paint.setColor(color);
2183 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2184}
2185
2186void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2187 const SkPaint& paint) {
2188 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002189
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 pts[0].set(x0, y0);
2191 pts[1].set(x1, y1);
2192 this->drawPoints(kLines_PointMode, 2, pts, paint);
2193}
2194
2195void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2196 SkScalar right, SkScalar bottom,
2197 const SkPaint& paint) {
2198 SkRect r;
2199
2200 r.set(left, top, right, bottom);
2201 this->drawRect(r, paint);
2202}
2203
2204void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2205 const SkPaint& paint) {
2206 if (radius < 0) {
2207 radius = 0;
2208 }
2209
2210 SkRect r;
2211 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002212 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213}
2214
2215void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2216 const SkPaint& paint) {
2217 if (rx > 0 && ry > 0) {
2218 if (paint.canComputeFastBounds()) {
2219 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002220 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221 return;
2222 }
2223 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002224 SkRRect rrect;
2225 rrect.setRectXY(r, rx, ry);
2226 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227 } else {
2228 this->drawRect(r, paint);
2229 }
2230}
2231
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2233 SkScalar sweepAngle, bool useCenter,
2234 const SkPaint& paint) {
2235 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2236 this->drawOval(oval, paint);
2237 } else {
2238 SkPath path;
2239 if (useCenter) {
2240 path.moveTo(oval.centerX(), oval.centerY());
2241 }
2242 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2243 if (useCenter) {
2244 path.close();
2245 }
2246 this->drawPath(path, paint);
2247 }
2248}
2249
2250void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2251 const SkPath& path, SkScalar hOffset,
2252 SkScalar vOffset, const SkPaint& paint) {
2253 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002254
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255 matrix.setTranslate(hOffset, vOffset);
2256 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2257}
2258
reed@android.comf76bacf2009-05-13 14:00:33 +00002259///////////////////////////////////////////////////////////////////////////////
2260
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263}
2264
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265///////////////////////////////////////////////////////////////////////////////
2266///////////////////////////////////////////////////////////////////////////////
2267
2268SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002269 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270
2271 SkASSERT(canvas);
2272
2273 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2274 fDone = !fImpl->next();
2275}
2276
2277SkCanvas::LayerIter::~LayerIter() {
2278 fImpl->~SkDrawIter();
2279}
2280
2281void SkCanvas::LayerIter::next() {
2282 fDone = !fImpl->next();
2283}
2284
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002285SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 return fImpl->getDevice();
2287}
2288
2289const SkMatrix& SkCanvas::LayerIter::matrix() const {
2290 return fImpl->getMatrix();
2291}
2292
2293const SkPaint& SkCanvas::LayerIter::paint() const {
2294 const SkPaint* paint = fImpl->getPaint();
2295 if (NULL == paint) {
2296 paint = &fDefaultPaint;
2297 }
2298 return *paint;
2299}
2300
2301const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2302int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2303int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002304
2305///////////////////////////////////////////////////////////////////////////////
2306
2307SkCanvas::ClipVisitor::~ClipVisitor() { }