blob: f5e91ffc43fa1b943f6ca62a59bdc2529c3cec21 [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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 return this->setDevice(device);
505}
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;
521 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
522 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
523}
524
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000525SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
527{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 inc_canvas();
529
530 this->init(device);
531}
532
533SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
535{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 inc_canvas();
537
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000538 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539}
540
541SkCanvas::~SkCanvas() {
542 // free up the contents of our deque
543 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000544 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000545
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 this->internalRestore(); // restore the last, since we're going away
547
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000548 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000549 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000550
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 dec_canvas();
552}
553
554SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
555 SkRefCnt_SafeAssign(fBounder, bounder);
556 return bounder;
557}
558
559SkDrawFilter* SkCanvas::getDrawFilter() const {
560 return fMCRec->fFilter;
561}
562
563SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
564 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
565 return filter;
566}
567
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000568SkMetaData& SkCanvas::getMetaData() {
569 // metadata users are rare, so we lazily allocate it. If that changes we
570 // can decide to just make it a field in the device (rather than a ptr)
571 if (NULL == fMetaData) {
572 fMetaData = new SkMetaData;
573 }
574 return *fMetaData;
575}
576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577///////////////////////////////////////////////////////////////////////////////
578
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000579void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000580 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000581 if (device) {
582 device->flush();
583 }
584}
585
reed@google.com210ce002011-11-01 14:24:23 +0000586SkISize SkCanvas::getDeviceSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000587 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000588 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
589}
590
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000591SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000593 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkASSERT(rec && rec->fLayer);
595 return rec->fLayer->fDevice;
596}
597
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000598SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000599 if (updateMatrixClip) {
600 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
601 }
reed@google.com9266fed2011-03-30 00:18:03 +0000602 return fMCRec->fTopLayer->fDevice;
603}
604
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000605SkBaseDevice* SkCanvas::setDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000607 SkDeque::F2BIter iter(fMCStack);
608 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000610 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
612 if (rootDevice == device) {
613 return device;
614 }
reed@google.com4b226022011-01-11 18:32:13 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000617 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 }
619 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000620 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 }
622
623 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
624 rootDevice = device;
625
626 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000627
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 /* Now we update our initial region to have the bounds of the new device,
629 and then intersect all of the clips in our stack with these bounds,
630 to ensure that we can't draw outside of the device's bounds (and trash
631 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000632
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 NOTE: this is only a partial-fix, since if the new device is larger than
634 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000635 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
637 reconstruct the correct clips, so this approximation will have to do.
638 The caller really needs to restore() back to the base if they want to
639 accurately take advantage of the new device bounds.
640 */
641
reed@google.com42aea282012-03-28 16:19:15 +0000642 SkIRect bounds;
643 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000645 } else {
646 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 }
reed@google.com42aea282012-03-28 16:19:15 +0000648 // now jam our 1st clip to be bounds, and intersect the rest with that
649 rec->fRasterClip->setRect(bounds);
650 while ((rec = (MCRec*)iter.next()) != NULL) {
651 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
652 }
653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 return device;
655}
656
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000657bool SkCanvas::readPixels(SkBitmap* bitmap,
658 int x, int y,
659 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000660 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000661 if (!device) {
662 return false;
663 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000664 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000665}
666
bsalomon@google.comc6980972011-11-02 19:57:21 +0000667bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000668 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000669 if (!device) {
670 return false;
671 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000672
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000673 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000674 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000675 if (!bounds.intersect(srcRect)) {
676 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000677 }
678
679 SkBitmap tmp;
680 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
681 bounds.height());
682 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
683 bitmap->swap(tmp);
684 return true;
685 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000686 return false;
687 }
reed@google.com51df9e32010-12-23 19:29:18 +0000688}
689
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000690void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
691 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000692 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000693 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000694 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
695 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
696 device->accessBitmap(true);
697 device->writePixels(bitmap, x, y, config8888);
698 }
reed@google.com51df9e32010-12-23 19:29:18 +0000699 }
700}
701
junov@google.com4370aed2012-01-18 16:21:08 +0000702SkCanvas* SkCanvas::canvasForDrawIter() {
703 return this;
704}
705
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706//////////////////////////////////////////////////////////////////////////////
707
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708void SkCanvas::updateDeviceCMCache() {
709 if (fDeviceCMDirty) {
710 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000711 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000713
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000715 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000717 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000719 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 } while ((layer = layer->fNext) != NULL);
721 }
722 fDeviceCMDirty = false;
723 }
724}
725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726///////////////////////////////////////////////////////////////////////////////
727
728int SkCanvas::internalSave(SaveFlags flags) {
729 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000730
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 MCRec* newTop = (MCRec*)fMCStack.push_back();
732 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 newTop->fNext = fMCRec;
735 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000736
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000737 if (SkCanvas::kClip_SaveFlag & flags) {
738 fClipStack.save();
739 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000740
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 return saveCount;
742}
743
744int SkCanvas::save(SaveFlags flags) {
745 // call shared impl
746 return this->internalSave(flags);
747}
748
749#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
750#define C16MASK (1 << SkBitmap::kRGB_565_Config)
751#define C8MASK (1 << SkBitmap::kA8_Config)
752
753static SkBitmap::Config resolve_config(SkCanvas* canvas,
754 const SkIRect& bounds,
755 SkCanvas::SaveFlags flags,
756 bool* isOpaque) {
757 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
758
759#if 0
760 // loop through and union all the configs we may draw into
761 uint32_t configMask = 0;
762 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
763 {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000764 SkBaseDevice* device = canvas->getLayerDevice(i);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 if (device->intersects(bounds))
766 configMask |= 1 << device->config();
767 }
768
769 // if the caller wants alpha or fullcolor, we can't return 565
770 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
771 SkCanvas::kHasAlphaLayer_SaveFlag))
772 configMask &= ~C16MASK;
773
774 switch (configMask) {
775 case C8MASK: // if we only have A8, return that
776 return SkBitmap::kA8_Config;
777
778 case C16MASK: // if we only have 565, return that
779 return SkBitmap::kRGB_565_Config;
780
781 default:
782 return SkBitmap::kARGB_8888_Config; // default answer
783 }
784#else
785 return SkBitmap::kARGB_8888_Config; // default answer
786#endif
787}
788
789static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
790 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
791}
792
junov@chromium.orga907ac32012-02-24 21:54:07 +0000793bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000794 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000795 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000796 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000797 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000798 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000799 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000800
801 if (imageFilter) {
802 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
803 // Filters may grow the bounds beyond the device bounds.
804 op = SkRegion::kReplace_Op;
805 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000806 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 if (NULL != bounds) {
808 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 this->getTotalMatrix().mapRect(&r, *bounds);
811 r.roundOut(&ir);
812 // early exit if the layer's bounds are clipped out
813 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000814 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000815 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000816 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000817 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 }
819 } else { // no user bounds, so just use the clip
820 ir = clipBounds;
821 }
822
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000823 fClipStack.clipDevRect(ir, op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000824
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 // early exit if the clip is now empty
826 if (bounds_affects_clip(flags) &&
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000827 !fMCRec->fRasterClip->op(ir, op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000828 return false;
829 }
830
831 if (intersection) {
832 *intersection = ir;
833 }
834 return true;
835}
836
837int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
838 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000839 return this->internalSaveLayer(bounds, paint, flags, false);
840}
841
reed@google.com76f10a32014-02-05 15:32:21 +0000842static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
843 SkBitmap::Config config,
844 int width, int height,
845 bool isOpaque) {
846 SkBaseDevice* device = canvas->getDevice();
847 if (device) {
848 return device->createCompatibleDevice(config, width, height, isOpaque);
849 } else {
850 return NULL;
851 }
852}
853
reed@google.com8926b162012-03-23 15:36:36 +0000854int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
855 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856 // do this before we create the layer. We don't call the public save() since
857 // that would invoke a possibly overridden virtual
858 int count = this->internalSave(flags);
859
860 fDeviceCMDirty = true;
861
862 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000863 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 return count;
865 }
866
reed@google.comb55deeb2012-01-06 14:43:09 +0000867 // Kill the imagefilter if our device doesn't allow it
868 SkLazyPaint lazyP;
869 if (paint && paint->getImageFilter()) {
870 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000871 if (justForImageFilter) {
872 // early exit if the layer was just for the imageFilter
873 return count;
874 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000875 SkPaint* p = lazyP.set(*paint);
876 p->setImageFilter(NULL);
877 paint = p;
878 }
879 }
880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 bool isOpaque;
882 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
883
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000884 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000885 if (paint && paint->getImageFilter()) {
reed@google.com76f10a32014-02-05 15:32:21 +0000886 device = createCompatibleDevice(this, config, ir.width(), ir.height(),
887 isOpaque);
reed@google.com76dd2772012-01-05 21:15:07 +0000888 } else {
889 device = this->createLayerDevice(config, ir.width(), ir.height(),
890 isOpaque);
891 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000892 if (NULL == device) {
893 SkDebugf("Unable to create device for layer.");
894 return count;
895 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000896
reed@google.com6f8f2922011-03-04 22:27:10 +0000897 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000898 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 device->unref();
900
901 layer->fNext = fMCRec->fTopLayer;
902 fMCRec->fLayer = layer;
903 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
904
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000905 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 return count;
907}
908
909int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
910 SaveFlags flags) {
911 if (0xFF == alpha) {
912 return this->saveLayer(bounds, NULL, flags);
913 } else {
914 SkPaint tmpPaint;
915 tmpPaint.setAlpha(alpha);
916 return this->saveLayer(bounds, &tmpPaint, flags);
917 }
918}
919
920void SkCanvas::restore() {
921 // check for underflow
922 if (fMCStack.count() > 1) {
923 this->internalRestore();
924 }
925}
926
927void SkCanvas::internalRestore() {
928 SkASSERT(fMCStack.count() != 0);
929
930 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000931 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000933 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
934 fClipStack.restore();
935 }
936
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000937 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 DeviceCM* layer = fMCRec->fLayer; // may be null
939 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
940 fMCRec->fLayer = NULL;
941
942 // now do the normal restore()
943 fMCRec->~MCRec(); // balanced in save()
944 fMCStack.pop_back();
945 fMCRec = (MCRec*)fMCStack.back();
946
947 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
948 since if we're being recorded, we don't want to record this (the
949 recorder will have already recorded the restore).
950 */
951 if (NULL != layer) {
952 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000953 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000954 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
955 layer->fPaint);
956 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000958
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000959 SkASSERT(fSaveLayerCount > 0);
960 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 }
962 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000963 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964}
965
966int SkCanvas::getSaveCount() const {
967 return fMCStack.count();
968}
969
970void SkCanvas::restoreToCount(int count) {
971 // sanity check
972 if (count < 1) {
973 count = 1;
974 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000975
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000976 int n = this->getSaveCount() - count;
977 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 this->restore();
979 }
980}
981
reed@google.com7c202932011-12-14 18:48:05 +0000982bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000983 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000984}
985
reed@google.com76f10a32014-02-05 15:32:21 +0000986SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
987 return this->onNewSurface(info);
988}
989
990SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
991 SkBaseDevice* dev = this->getDevice();
992 return dev ? dev->newSurface(info) : NULL;
993}
994
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000995SkImageInfo SkCanvas::imageInfo() const {
996 SkBaseDevice* dev = this->getDevice();
997 if (dev) {
998 return dev->imageInfo();
999 } else {
1000 // TODO: need a real unknown for alphatype it seems.
1001 SkAlphaType unknownAlphaType = kIgnore_SkAlphaType;
1002 return SkImageInfo::Make(0, 0, kUnknown_SkColorType, unknownAlphaType);
1003 }
1004}
1005
1006const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1007 return this->onPeekPixels(info, rowBytes);
1008}
1009
1010const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1011 SkBaseDevice* dev = this->getDevice();
1012 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1013}
1014
1015SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1016 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1017 if (NULL == fAddr) {
1018 fInfo = canvas->imageInfo();
1019 if (kUnknown_SkColorType == fInfo.colorType() ||
1020 !fBitmap.allocPixels(fInfo))
1021 {
1022 return; // failure, fAddr is NULL
1023 }
1024 fBitmap.lockPixels();
1025 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1026 return; // failure, fAddr is NULL
1027 }
1028 fAddr = fBitmap.getPixels();
1029 fRowBytes = fBitmap.rowBytes();
1030 }
1031 SkASSERT(fAddr); // success
1032}
1033
1034bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1035 if (fAddr) {
1036 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1037 NULL, NULL);
1038 } else {
1039 bitmap->reset();
1040 return false;
1041 }
1042}
1043
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044/////////////////////////////////////////////////////////////////////////////
1045
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001046void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001048 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 return;
1050 }
1051
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001052 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001054 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001056
1057 SkDEBUGCODE(bitmap.validate();)
1058 CHECK_LOCKCOUNT_BALANCE(bitmap);
1059
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001060 SkRect storage;
1061 const SkRect* bounds = NULL;
1062 if (paint && paint->canComputeFastBounds()) {
1063 bitmap.getBounds(&storage);
1064 matrix.mapRect(&storage);
1065 bounds = &paint->computeFastBounds(storage, &storage);
1066 }
1067
1068 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001069
1070 while (iter.next()) {
1071 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1072 }
1073
1074 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075}
1076
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001077void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001078 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 SkPaint tmp;
1080 if (NULL == paint) {
1081 tmp.setDither(true);
1082 paint = &tmp;
1083 }
reed@google.com4b226022011-01-11 18:32:13 +00001084
reed@google.com8926b162012-03-23 15:36:36 +00001085 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001087 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001088 paint = &looper.paint();
1089 SkImageFilter* filter = paint->getImageFilter();
1090 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001091 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001092 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001093 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001094 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001095 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001096 SkMatrix matrix = *iter.fMatrix;
1097 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001098 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001099 SkPaint tmpUnfiltered(*paint);
1100 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001101 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1102 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001103 }
1104 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001105 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001106 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001108 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109}
1110
reed@google.com8926b162012-03-23 15:36:36 +00001111void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1112 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001113 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001114 return;
1115 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001116 SkDEBUGCODE(bitmap.validate();)
1117 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001118
reed@google.com8926b162012-03-23 15:36:36 +00001119 SkPaint tmp;
1120 if (NULL == paint) {
1121 paint = &tmp;
1122 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001123
reed@google.com8926b162012-03-23 15:36:36 +00001124 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001125
reed@google.com8926b162012-03-23 15:36:36 +00001126 while (iter.next()) {
1127 paint = &looper.paint();
1128 SkImageFilter* filter = paint->getImageFilter();
1129 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1130 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001131 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001132 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001133 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001134 SkMatrix matrix = *iter.fMatrix;
1135 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001136 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001137 SkPaint tmpUnfiltered(*paint);
1138 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001139 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001140 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001141 }
1142 } else {
1143 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1144 }
1145 }
1146 LOOPER_END
1147}
1148
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149/////////////////////////////////////////////////////////////////////////////
1150
1151bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1152 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001153 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 return fMCRec->fMatrix->preTranslate(dx, dy);
1155}
1156
1157bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1158 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001159 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 return fMCRec->fMatrix->preScale(sx, sy);
1161}
1162
1163bool SkCanvas::rotate(SkScalar degrees) {
1164 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001165 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 return fMCRec->fMatrix->preRotate(degrees);
1167}
1168
1169bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1170 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001171 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172 return fMCRec->fMatrix->preSkew(sx, sy);
1173}
1174
1175bool SkCanvas::concat(const SkMatrix& matrix) {
1176 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001177 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 return fMCRec->fMatrix->preConcat(matrix);
1179}
1180
1181void SkCanvas::setMatrix(const SkMatrix& matrix) {
1182 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001183 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 *fMCRec->fMatrix = matrix;
1185}
1186
1187// this is not virtual, so it must call a virtual method so that subclasses
1188// will see its action
1189void SkCanvas::resetMatrix() {
1190 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001191
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 matrix.reset();
1193 this->setMatrix(matrix);
1194}
1195
1196//////////////////////////////////////////////////////////////////////////////
1197
reed@google.comc42d35d2011-10-12 11:57:42 +00001198bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001199#ifdef SK_ENABLE_CLIP_QUICKREJECT
1200 if (SkRegion::kIntersect_Op == op) {
1201 if (fMCRec->fRasterClip->isEmpty()) {
1202 return false;
1203 }
1204
reed@google.com3b3e8952012-08-16 20:53:31 +00001205 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001206 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001207 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001208
1209 fClipStack.clipEmpty();
1210 return fMCRec->fRasterClip->setEmpty();
1211 }
1212 }
1213#endif
1214
reed@google.com5c3d1472011-02-22 19:12:23 +00001215 AutoValidateClip avc(this);
1216
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001218 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001219 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220
1221 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001222 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001223 // the matrix. This means we don't have to a) make a path, and b) tell
1224 // the region code to scan-convert the path, only to discover that it
1225 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227
1228 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001229 fClipStack.clipDevRect(r, op, doAA);
1230 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001232 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001233 // and clip against that, since it can handle any matrix. However, to
1234 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1235 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 SkPath path;
1237
1238 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001239 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 }
1241}
1242
reed@google.com00177082011-10-12 14:34:30 +00001243static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001244 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001245 // base is used to limit the size (and therefore memory allocation) of the
1246 // region that results from scan converting devPath.
1247 SkRegion base;
1248
reed@google.com819c9212011-02-23 18:56:55 +00001249 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001250 // since we are intersect, we can do better (tighter) with currRgn's
1251 // bounds, than just using the device. However, if currRgn is complex,
1252 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001253 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001254 // FIXME: we should also be able to do this when currClip->isBW(),
1255 // but relaxing the test above triggers GM asserts in
1256 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1257 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001258 } else {
reed@google.com00177082011-10-12 14:34:30 +00001259 base.setRect(currClip->getBounds());
1260 SkRasterClip clip;
1261 clip.setPath(devPath, base, doAA);
1262 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001263 }
reed@google.com819c9212011-02-23 18:56:55 +00001264 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001265 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001266 if (!device) {
1267 return currClip->setEmpty();
1268 }
1269
junov@chromium.orga907ac32012-02-24 21:54:07 +00001270 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001271
1272 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001273 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001274 } else {
reed@google.com00177082011-10-12 14:34:30 +00001275 SkRasterClip clip;
1276 clip.setPath(devPath, base, doAA);
1277 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001278 }
1279 }
1280}
1281
reed@google.com4ed0fb72012-12-12 20:48:18 +00001282bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1283 if (rrect.isRect()) {
1284 // call the non-virtual version
1285 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1286 } else {
1287 SkPath path;
1288 path.addRRect(rrect);
1289 // call the non-virtual version
1290 return this->SkCanvas::clipPath(path, op, doAA);
1291 }
1292}
1293
reed@google.comc42d35d2011-10-12 11:57:42 +00001294bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001295#ifdef SK_ENABLE_CLIP_QUICKREJECT
1296 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1297 if (fMCRec->fRasterClip->isEmpty()) {
1298 return false;
1299 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001300
reed@google.com3b3e8952012-08-16 20:53:31 +00001301 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001302 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001303 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001304
reed@google.comda17f752012-08-16 18:27:05 +00001305 fClipStack.clipEmpty();
1306 return fMCRec->fRasterClip->setEmpty();
1307 }
1308 }
1309#endif
1310
reed@google.com5c3d1472011-02-22 19:12:23 +00001311 AutoValidateClip avc(this);
1312
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001314 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001315 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316
1317 SkPath devPath;
1318 path.transform(*fMCRec->fMatrix, &devPath);
1319
reed@google.comfe701122011-11-08 19:41:23 +00001320 // Check if the transfomation, or the original path itself
1321 // made us empty. Note this can also happen if we contained NaN
1322 // values. computing the bounds detects this, and will set our
1323 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1324 if (devPath.getBounds().isEmpty()) {
1325 // resetting the path will remove any NaN or other wanky values
1326 // that might upset our scan converter.
1327 devPath.reset();
1328 }
1329
reed@google.com5c3d1472011-02-22 19:12:23 +00001330 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001331 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001332
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001333 if (fAllowSimplifyClip) {
1334 devPath.reset();
1335 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1336 const SkClipStack* clipStack = getClipStack();
1337 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1338 const SkClipStack::Element* element;
1339 while ((element = iter.next())) {
1340 SkClipStack::Element::Type type = element->getType();
1341 if (type == SkClipStack::Element::kEmpty_Type) {
1342 continue;
1343 }
1344 SkPath operand;
1345 if (type == SkClipStack::Element::kRect_Type) {
1346 operand.addRect(element->getRect());
1347 } else if (type == SkClipStack::Element::kPath_Type) {
1348 operand = element->getPath();
1349 } else {
1350 SkDEBUGFAIL("Unexpected type.");
1351 }
1352 SkRegion::Op elementOp = element->getOp();
1353 if (elementOp == SkRegion::kReplace_Op) {
1354 devPath = operand;
1355 } else {
1356 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1357 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001358 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1359 // perhaps we need an API change to avoid this sort of mixed-signals about
1360 // clipping.
1361 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001362 }
1363 op = SkRegion::kReplace_Op;
1364 }
1365
reed@google.com00177082011-10-12 14:34:30 +00001366 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367}
1368
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001369bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1370 bool inverseFilled) {
1371 // This is for updating the clip conservatively using only bounds
1372 // information.
1373 // Contract:
1374 // The current clip must contain the true clip. The true
1375 // clip is the clip that would have normally been computed
1376 // by calls to clipPath and clipRRect
1377 // Objective:
1378 // Keep the current clip as small as possible without
1379 // breaking the contract, using only clip bounding rectangles
1380 // (for performance).
1381
1382 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1383 // don't have to worry about getting caught in a loop. Thus anywhere
1384 // we call a virtual method, we explicitly prefix it with
1385 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001386
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001387 if (inverseFilled) {
1388 switch (op) {
1389 case SkRegion::kIntersect_Op:
1390 case SkRegion::kDifference_Op:
1391 // These ops can only shrink the current clip. So leaving
1392 // the clip unchanges conservatively respects the contract.
1393 return this->getClipDeviceBounds(NULL);
1394 case SkRegion::kUnion_Op:
1395 case SkRegion::kReplace_Op:
1396 case SkRegion::kReverseDifference_Op:
1397 case SkRegion::kXOR_Op:
1398 {
1399 // These ops can grow the current clip up to the extents of
1400 // the input clip, which is inverse filled, so we just set
1401 // the current clip to the device bounds.
1402 SkRect deviceBounds;
1403 SkIRect deviceIBounds;
1404 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001405 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001406 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1407 // set the clip in device space
1408 this->SkCanvas::setMatrix(SkMatrix::I());
1409 bool result = this->SkCanvas::clipRect(deviceBounds,
1410 SkRegion::kReplace_Op, false);
1411 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1412 return result;
1413 }
1414 default:
1415 SkASSERT(0); // unhandled op?
1416 }
1417 } else {
1418 // Not inverse filled
1419 switch (op) {
1420 case SkRegion::kIntersect_Op:
1421 case SkRegion::kUnion_Op:
1422 case SkRegion::kReplace_Op:
1423 return this->SkCanvas::clipRect(bounds, op, false);
1424 case SkRegion::kDifference_Op:
1425 // Difference can only shrink the current clip.
1426 // Leaving clip unchanged conservatively fullfills the contract.
1427 return this->getClipDeviceBounds(NULL);
1428 case SkRegion::kReverseDifference_Op:
1429 // To reverse, we swap in the bounds with a replace op.
1430 // As with difference, leave it unchanged.
1431 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1432 case SkRegion::kXOR_Op:
1433 // Be conservative, based on (A XOR B) always included in (A union B),
1434 // which is always included in (bounds(A) union bounds(B))
1435 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1436 default:
1437 SkASSERT(0); // unhandled op?
1438 }
1439 }
1440 return true;
1441}
1442
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001444 AutoValidateClip avc(this);
1445
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001447 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448
reed@google.com5c3d1472011-02-22 19:12:23 +00001449 // todo: signal fClipStack that we have a region, and therefore (I guess)
1450 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001451 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001452
reed@google.com00177082011-10-12 14:34:30 +00001453 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454}
1455
reed@google.com819c9212011-02-23 18:56:55 +00001456#ifdef SK_DEBUG
1457void SkCanvas::validateClip() const {
1458 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001459 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001460 if (!device) {
1461 SkASSERT(this->getTotalClip().isEmpty());
1462 return;
1463 }
1464
reed@google.com819c9212011-02-23 18:56:55 +00001465 SkIRect ir;
1466 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001467 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001468
robertphillips@google.com80214e22012-07-20 15:33:18 +00001469 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001470 const SkClipStack::Element* element;
1471 while ((element = iter.next()) != NULL) {
1472 switch (element->getType()) {
1473 case SkClipStack::Element::kPath_Type:
1474 clipPathHelper(this,
1475 &tmpClip,
1476 element->getPath(),
1477 element->getOp(),
1478 element->isAA());
1479 break;
1480 case SkClipStack::Element::kRect_Type:
1481 element->getRect().round(&ir);
1482 tmpClip.op(ir, element->getOp());
1483 break;
1484 case SkClipStack::Element::kEmpty_Type:
1485 tmpClip.setEmpty();
1486 break;
reed@google.com819c9212011-02-23 18:56:55 +00001487 }
1488 }
1489
reed@google.com6f8f2922011-03-04 22:27:10 +00001490#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001491 // now compare against the current rgn
1492 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001493 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001494#endif
reed@google.com819c9212011-02-23 18:56:55 +00001495}
1496#endif
1497
reed@google.com90c07ea2012-04-13 13:50:27 +00001498void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001499 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001500 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001501
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001502 static const SkRect kEmpty = { 0, 0, 0, 0 };
1503 while ((element = iter.next()) != NULL) {
1504 switch (element->getType()) {
1505 case SkClipStack::Element::kPath_Type:
1506 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1507 break;
1508 case SkClipStack::Element::kRect_Type:
1509 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1510 break;
1511 case SkClipStack::Element::kEmpty_Type:
1512 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1513 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001514 }
1515 }
1516}
1517
reed@google.com5c3d1472011-02-22 19:12:23 +00001518///////////////////////////////////////////////////////////////////////////////
1519
reed@google.com3b3e8952012-08-16 20:53:31 +00001520bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001521
reed@google.com16078632011-12-06 18:56:37 +00001522 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001523 return true;
1524
reed@google.com00177082011-10-12 14:34:30 +00001525 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526 return true;
1527 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528
tomhudson@google.com8d430182011-06-06 19:11:19 +00001529 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001530 SkRect dst;
1531 fMCRec->fMatrix->mapRect(&dst, rect);
1532 SkIRect idst;
1533 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001534 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001535 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001536 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001537
reed@android.coma380ae42009-07-21 01:17:02 +00001538 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001539 // TODO: should we use | instead, or compare all 4 at once?
1540 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001541 return true;
1542 }
reed@google.comc0784db2013-12-13 21:16:12 +00001543 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001544 return true;
1545 }
1546 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548}
1549
reed@google.com3b3e8952012-08-16 20:53:31 +00001550bool SkCanvas::quickReject(const SkPath& path) const {
1551 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552}
1553
reed@google.com3b3e8952012-08-16 20:53:31 +00001554bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001555 SkIRect ibounds;
1556 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557 return false;
1558 }
1559
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001560 SkMatrix inverse;
1561 // if we can't invert the CTM, we can't return local clip bounds
1562 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001563 if (bounds) {
1564 bounds->setEmpty();
1565 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001566 return false;
1567 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001569 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001570 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001571 // adjust it outwards in case we are antialiasing
1572 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001573
reed@google.com8f4d2302013-12-17 16:44:46 +00001574 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1575 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576 inverse.mapRect(bounds, r);
1577 }
1578 return true;
1579}
1580
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001581bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001582 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001583 if (clip.isEmpty()) {
1584 if (bounds) {
1585 bounds->setEmpty();
1586 }
1587 return false;
1588 }
1589
1590 if (NULL != bounds) {
1591 *bounds = clip.getBounds();
1592 }
1593 return true;
1594}
1595
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596const SkMatrix& SkCanvas::getTotalMatrix() const {
1597 return *fMCRec->fMatrix;
1598}
1599
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001600SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001601 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1602 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001603 return kComplex_ClipType;
1604}
1605
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001607 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001610SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001611 int width, int height,
1612 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001613 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001614 if (device) {
1615 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1616 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001617 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001618 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001619 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620}
1621
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001622GrContext* SkCanvas::getGrContext() {
1623#if SK_SUPPORT_GPU
1624 SkBaseDevice* device = this->getTopDevice();
1625 if (NULL != device) {
1626 GrRenderTarget* renderTarget = device->accessRenderTarget();
1627 if (NULL != renderTarget) {
1628 return renderTarget->getContext();
1629 }
1630 }
1631#endif
1632
1633 return NULL;
1634
1635}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001636
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637//////////////////////////////////////////////////////////////////////////////
1638// These are the virtual drawing methods
1639//////////////////////////////////////////////////////////////////////////////
1640
reed@google.com2a981812011-04-14 18:59:28 +00001641void SkCanvas::clear(SkColor color) {
1642 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001643 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001644 while (iter.next()) {
1645 iter.fDevice->clear(color);
1646 }
1647}
1648
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001650 this->internalDrawPaint(paint);
1651}
1652
1653void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001654 CHECK_SHADER_NOSETCONTEXT(paint);
1655
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001656 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657
1658 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001659 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660 }
1661
reed@google.com4e2b3d32011-04-07 14:18:59 +00001662 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663}
1664
1665void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1666 const SkPaint& paint) {
1667 if ((long)count <= 0) {
1668 return;
1669 }
1670
reed@google.comea033602012-12-14 13:13:55 +00001671 CHECK_SHADER_NOSETCONTEXT(paint);
1672
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001673 SkRect r, storage;
1674 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001675 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001676 // special-case 2 points (common for drawing a single line)
1677 if (2 == count) {
1678 r.set(pts[0], pts[1]);
1679 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001680 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001681 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001682 bounds = &paint.computeFastStrokeBounds(r, &storage);
1683 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001684 return;
1685 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001686 }
reed@google.coma584aed2012-05-16 14:06:02 +00001687
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688 SkASSERT(pts != NULL);
1689
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001690 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001691
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001693 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694 }
reed@google.com4b226022011-01-11 18:32:13 +00001695
reed@google.com4e2b3d32011-04-07 14:18:59 +00001696 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697}
1698
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001699void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001700 CHECK_SHADER_NOSETCONTEXT(paint);
1701
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001702 SkRect storage;
1703 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001705 bounds = &paint.computeFastBounds(r, &storage);
1706 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707 return;
1708 }
1709 }
reed@google.com4b226022011-01-11 18:32:13 +00001710
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001711 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712
1713 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001714 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715 }
1716
reed@google.com4e2b3d32011-04-07 14:18:59 +00001717 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718}
1719
reed@google.com4ed0fb72012-12-12 20:48:18 +00001720void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001721 CHECK_SHADER_NOSETCONTEXT(paint);
1722
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001723 SkRect storage;
1724 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001725 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001726 bounds = &paint.computeFastBounds(oval, &storage);
1727 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001728 return;
1729 }
1730 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001731
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001732 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001733
1734 while (iter.next()) {
1735 iter.fDevice->drawOval(iter, oval, looper.paint());
1736 }
1737
1738 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001739}
1740
1741void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001742 CHECK_SHADER_NOSETCONTEXT(paint);
1743
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001744 SkRect storage;
1745 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001746 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001747 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1748 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001749 return;
1750 }
1751 }
1752
1753 if (rrect.isRect()) {
1754 // call the non-virtual version
1755 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001756 return;
1757 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001758 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001759 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1760 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001761 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001762
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001763 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001764
1765 while (iter.next()) {
1766 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1767 }
1768
1769 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001770}
1771
1772
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001773void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001774 CHECK_SHADER_NOSETCONTEXT(paint);
1775
reed@google.com93645112012-07-26 16:11:47 +00001776 if (!path.isFinite()) {
1777 return;
1778 }
1779
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001780 SkRect storage;
1781 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001782 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001783 const SkRect& pathBounds = path.getBounds();
1784 bounds = &paint.computeFastBounds(pathBounds, &storage);
1785 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 return;
1787 }
1788 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001789 if (path.isEmpty()) {
1790 if (path.isInverseFillType()) {
1791 this->internalDrawPaint(paint);
1792 }
1793 return;
1794 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797
1798 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001799 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 }
1801
reed@google.com4e2b3d32011-04-07 14:18:59 +00001802 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803}
1804
1805void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1806 const SkPaint* paint) {
1807 SkDEBUGCODE(bitmap.validate();)
1808
reed@google.com3d608122011-11-21 15:16:16 +00001809 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001810 SkRect bounds = {
1811 x, y,
1812 x + SkIntToScalar(bitmap.width()),
1813 y + SkIntToScalar(bitmap.height())
1814 };
1815 if (paint) {
1816 (void)paint->computeFastBounds(bounds, &bounds);
1817 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001818 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819 return;
1820 }
1821 }
reed@google.com4b226022011-01-11 18:32:13 +00001822
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 SkMatrix matrix;
1824 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001825 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826}
1827
reed@google.com9987ec32011-09-07 11:57:52 +00001828// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001829void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001830 const SkRect& dst, const SkPaint* paint,
1831 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001832 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 return;
1834 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001835
reed@google.comea033602012-12-14 13:13:55 +00001836 CHECK_LOCKCOUNT_BALANCE(bitmap);
1837
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001838 SkRect storage;
1839 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001840 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001841 if (paint) {
1842 bounds = &paint->computeFastBounds(dst, &storage);
1843 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001844 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001845 return;
1846 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847 }
reed@google.com3d608122011-11-21 15:16:16 +00001848
reed@google.com33535f32012-09-25 15:37:50 +00001849 SkLazyPaint lazy;
1850 if (NULL == paint) {
1851 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001853
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001854 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001855
reed@google.com33535f32012-09-25 15:37:50 +00001856 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001857 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001858 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001859
reed@google.com33535f32012-09-25 15:37:50 +00001860 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861}
1862
reed@google.com71121732012-09-18 15:14:33 +00001863void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001864 const SkRect& dst, const SkPaint* paint,
1865 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001866 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001867 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001868}
1869
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1871 const SkPaint* paint) {
1872 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001873 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874}
1875
reed@google.com9987ec32011-09-07 11:57:52 +00001876void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1877 const SkIRect& center, const SkRect& dst,
1878 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001879 if (bitmap.drawsNothing()) {
1880 return;
1881 }
reed@google.com3d608122011-11-21 15:16:16 +00001882 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001883 SkRect storage;
1884 const SkRect* bounds = &dst;
1885 if (paint) {
1886 bounds = &paint->computeFastBounds(dst, &storage);
1887 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001888 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001889 return;
1890 }
1891 }
1892
reed@google.com9987ec32011-09-07 11:57:52 +00001893 const int32_t w = bitmap.width();
1894 const int32_t h = bitmap.height();
1895
1896 SkIRect c = center;
1897 // pin center to the bounds of the bitmap
1898 c.fLeft = SkMax32(0, center.fLeft);
1899 c.fTop = SkMax32(0, center.fTop);
1900 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1901 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1902
reed@google.com71121732012-09-18 15:14:33 +00001903 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001904 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001905 };
1906 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001907 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001908 };
reed@google.com9987ec32011-09-07 11:57:52 +00001909 SkScalar dstX[4] = {
1910 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1911 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1912 };
1913 SkScalar dstY[4] = {
1914 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1915 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1916 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001917
reed@google.com9987ec32011-09-07 11:57:52 +00001918 if (dstX[1] > dstX[2]) {
1919 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1920 dstX[2] = dstX[1];
1921 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001922
reed@google.com9987ec32011-09-07 11:57:52 +00001923 if (dstY[1] > dstY[2]) {
1924 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1925 dstY[2] = dstY[1];
1926 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001927
reed@google.com9987ec32011-09-07 11:57:52 +00001928 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001929 SkRect s, d;
1930
reed@google.com9987ec32011-09-07 11:57:52 +00001931 s.fTop = srcY[y];
1932 s.fBottom = srcY[y+1];
1933 d.fTop = dstY[y];
1934 d.fBottom = dstY[y+1];
1935 for (int x = 0; x < 3; x++) {
1936 s.fLeft = srcX[x];
1937 s.fRight = srcX[x+1];
1938 d.fLeft = dstX[x];
1939 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001940 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001941 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001942 }
1943 }
1944}
1945
1946void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1947 const SkRect& dst, const SkPaint* paint) {
1948 SkDEBUGCODE(bitmap.validate();)
1949
1950 // Need a device entry-point, so gpu can use a mesh
1951 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1952}
1953
reed@google.comf67e4cf2011-03-15 20:56:58 +00001954class SkDeviceFilteredPaint {
1955public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001956 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1957 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001958 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001959 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001960 newPaint->setFlags(flags.fFlags);
1961 newPaint->setHinting(flags.fHinting);
1962 fPaint = newPaint;
1963 } else {
1964 fPaint = &paint;
1965 }
1966 }
1967
reed@google.comf67e4cf2011-03-15 20:56:58 +00001968 const SkPaint& paint() const { return *fPaint; }
1969
1970private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001971 const SkPaint* fPaint;
1972 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001973};
1974
bungeman@google.com52c748b2011-08-22 21:30:43 +00001975void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1976 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001977 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001978 draw.fDevice->drawRect(draw, r, paint);
1979 } else {
1980 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001981 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001982 draw.fDevice->drawRect(draw, r, p);
1983 }
1984}
1985
1986void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1987 const char text[], size_t byteLength,
1988 SkScalar x, SkScalar y) {
1989 SkASSERT(byteLength == 0 || text != NULL);
1990
1991 // nothing to draw
1992 if (text == NULL || byteLength == 0 ||
1993 draw.fClip->isEmpty() ||
1994 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1995 return;
1996 }
1997
1998 SkScalar width = 0;
1999 SkPoint start;
2000
2001 start.set(0, 0); // to avoid warning
2002 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2003 SkPaint::kStrikeThruText_Flag)) {
2004 width = paint.measureText(text, byteLength);
2005
2006 SkScalar offsetX = 0;
2007 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2008 offsetX = SkScalarHalf(width);
2009 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2010 offsetX = width;
2011 }
2012 start.set(x - offsetX, y);
2013 }
2014
2015 if (0 == width) {
2016 return;
2017 }
2018
2019 uint32_t flags = paint.getFlags();
2020
2021 if (flags & (SkPaint::kUnderlineText_Flag |
2022 SkPaint::kStrikeThruText_Flag)) {
2023 SkScalar textSize = paint.getTextSize();
2024 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2025 SkRect r;
2026
2027 r.fLeft = start.fX;
2028 r.fRight = start.fX + width;
2029
2030 if (flags & SkPaint::kUnderlineText_Flag) {
2031 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2032 start.fY);
2033 r.fTop = offset;
2034 r.fBottom = offset + height;
2035 DrawRect(draw, paint, r, textSize);
2036 }
2037 if (flags & SkPaint::kStrikeThruText_Flag) {
2038 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2039 start.fY);
2040 r.fTop = offset;
2041 r.fBottom = offset + height;
2042 DrawRect(draw, paint, r, textSize);
2043 }
2044 }
2045}
2046
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047void SkCanvas::drawText(const void* text, size_t byteLength,
2048 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002049 CHECK_SHADER_NOSETCONTEXT(paint);
2050
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002051 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002052
2053 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002054 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002055 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002056 DrawTextDecorations(iter, dfp.paint(),
2057 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058 }
2059
reed@google.com4e2b3d32011-04-07 14:18:59 +00002060 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061}
2062
2063void SkCanvas::drawPosText(const void* text, size_t byteLength,
2064 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002065 CHECK_SHADER_NOSETCONTEXT(paint);
2066
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002067 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002068
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002070 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002072 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 }
reed@google.com4b226022011-01-11 18:32:13 +00002074
reed@google.com4e2b3d32011-04-07 14:18:59 +00002075 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076}
2077
2078void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2079 const SkScalar xpos[], SkScalar constY,
2080 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002081 CHECK_SHADER_NOSETCONTEXT(paint);
2082
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002083 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002084
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002086 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002087 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002088 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 }
reed@google.com4b226022011-01-11 18:32:13 +00002090
reed@google.com4e2b3d32011-04-07 14:18:59 +00002091 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092}
2093
2094void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2095 const SkPath& path, const SkMatrix* matrix,
2096 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002097 CHECK_SHADER_NOSETCONTEXT(paint);
2098
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002099 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100
2101 while (iter.next()) {
2102 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002103 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104 }
2105
reed@google.com4e2b3d32011-04-07 14:18:59 +00002106 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107}
2108
2109void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2110 const SkPoint verts[], const SkPoint texs[],
2111 const SkColor colors[], SkXfermode* xmode,
2112 const uint16_t indices[], int indexCount,
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::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002117
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118 while (iter.next()) {
2119 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002120 colors, xmode, indices, indexCount,
2121 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 }
reed@google.com4b226022011-01-11 18:32:13 +00002123
reed@google.com4e2b3d32011-04-07 14:18:59 +00002124 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125}
2126
2127//////////////////////////////////////////////////////////////////////////////
2128// These methods are NOT virtual, and therefore must call back into virtual
2129// methods, rather than actually drawing themselves.
2130//////////////////////////////////////////////////////////////////////////////
2131
2132void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002133 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134 SkPaint paint;
2135
2136 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002137 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002138 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139 }
2140 this->drawPaint(paint);
2141}
2142
reed@android.com845fdac2009-06-23 03:01:32 +00002143void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002144 SkPaint paint;
2145
2146 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002147 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002148 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 }
2150 this->drawPaint(paint);
2151}
2152
2153void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2154 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002155
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156 pt.set(x, y);
2157 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2158}
2159
2160void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2161 SkPoint pt;
2162 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002163
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164 pt.set(x, y);
2165 paint.setColor(color);
2166 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2167}
2168
2169void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2170 const SkPaint& paint) {
2171 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002172
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 pts[0].set(x0, y0);
2174 pts[1].set(x1, y1);
2175 this->drawPoints(kLines_PointMode, 2, pts, paint);
2176}
2177
2178void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2179 SkScalar right, SkScalar bottom,
2180 const SkPaint& paint) {
2181 SkRect r;
2182
2183 r.set(left, top, right, bottom);
2184 this->drawRect(r, paint);
2185}
2186
2187void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2188 const SkPaint& paint) {
2189 if (radius < 0) {
2190 radius = 0;
2191 }
2192
2193 SkRect r;
2194 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002195 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196}
2197
2198void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2199 const SkPaint& paint) {
2200 if (rx > 0 && ry > 0) {
2201 if (paint.canComputeFastBounds()) {
2202 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002203 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204 return;
2205 }
2206 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002207 SkRRect rrect;
2208 rrect.setRectXY(r, rx, ry);
2209 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210 } else {
2211 this->drawRect(r, paint);
2212 }
2213}
2214
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2216 SkScalar sweepAngle, bool useCenter,
2217 const SkPaint& paint) {
2218 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2219 this->drawOval(oval, paint);
2220 } else {
2221 SkPath path;
2222 if (useCenter) {
2223 path.moveTo(oval.centerX(), oval.centerY());
2224 }
2225 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2226 if (useCenter) {
2227 path.close();
2228 }
2229 this->drawPath(path, paint);
2230 }
2231}
2232
2233void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2234 const SkPath& path, SkScalar hOffset,
2235 SkScalar vOffset, const SkPaint& paint) {
2236 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002237
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238 matrix.setTranslate(hOffset, vOffset);
2239 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2240}
2241
reed@android.comf76bacf2009-05-13 14:00:33 +00002242///////////////////////////////////////////////////////////////////////////////
2243
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246}
2247
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248///////////////////////////////////////////////////////////////////////////////
2249///////////////////////////////////////////////////////////////////////////////
2250
2251SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002252 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253
2254 SkASSERT(canvas);
2255
2256 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2257 fDone = !fImpl->next();
2258}
2259
2260SkCanvas::LayerIter::~LayerIter() {
2261 fImpl->~SkDrawIter();
2262}
2263
2264void SkCanvas::LayerIter::next() {
2265 fDone = !fImpl->next();
2266}
2267
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002268SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 return fImpl->getDevice();
2270}
2271
2272const SkMatrix& SkCanvas::LayerIter::matrix() const {
2273 return fImpl->getMatrix();
2274}
2275
2276const SkPaint& SkCanvas::LayerIter::paint() const {
2277 const SkPaint* paint = fImpl->getPaint();
2278 if (NULL == paint) {
2279 paint = &fDefaultPaint;
2280 }
2281 return *paint;
2282}
2283
2284const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2285int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2286int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002287
2288///////////////////////////////////////////////////////////////////////////////
2289
2290SkCanvas::ClipVisitor::~ClipVisitor() { }