blob: cffc46aed8c1819ba341b7511b4d6157e0057b2b [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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
750 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
751}
752
junov@chromium.orga907ac32012-02-24 21:54:07 +0000753bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000754 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000755 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000756 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000757 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000758 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000759 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000760
761 if (imageFilter) {
762 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
763 // Filters may grow the bounds beyond the device bounds.
764 op = SkRegion::kReplace_Op;
765 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000766 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 if (NULL != bounds) {
768 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000769
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 this->getTotalMatrix().mapRect(&r, *bounds);
771 r.roundOut(&ir);
772 // early exit if the layer's bounds are clipped out
773 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000774 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000775 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000776 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000777 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 }
779 } else { // no user bounds, so just use the clip
780 ir = clipBounds;
781 }
782
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000783 fClipStack.clipDevRect(ir, op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 // early exit if the clip is now empty
786 if (bounds_affects_clip(flags) &&
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000787 !fMCRec->fRasterClip->op(ir, op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000788 return false;
789 }
790
791 if (intersection) {
792 *intersection = ir;
793 }
794 return true;
795}
796
797int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
798 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000799 return this->internalSaveLayer(bounds, paint, flags, false);
800}
801
reed@google.com76f10a32014-02-05 15:32:21 +0000802static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000803 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000804 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000805 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000806}
807
reed@google.com8926b162012-03-23 15:36:36 +0000808int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
809 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 // do this before we create the layer. We don't call the public save() since
811 // that would invoke a possibly overridden virtual
812 int count = this->internalSave(flags);
813
814 fDeviceCMDirty = true;
815
816 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000817 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 return count;
819 }
820
reed@google.comb55deeb2012-01-06 14:43:09 +0000821 // Kill the imagefilter if our device doesn't allow it
822 SkLazyPaint lazyP;
823 if (paint && paint->getImageFilter()) {
824 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000825 if (justForImageFilter) {
826 // early exit if the layer was just for the imageFilter
827 return count;
828 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000829 SkPaint* p = lazyP.set(*paint);
830 p->setImageFilter(NULL);
831 paint = p;
832 }
833 }
834
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000835 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
836 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
837 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000839 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000840 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000841 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000842 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000843 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000844 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000845 if (NULL == device) {
846 SkDebugf("Unable to create device for layer.");
847 return count;
848 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000849
reed@google.com6f8f2922011-03-04 22:27:10 +0000850 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000851 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 device->unref();
853
854 layer->fNext = fMCRec->fTopLayer;
855 fMCRec->fLayer = layer;
856 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
857
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000858 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 return count;
860}
861
862int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
863 SaveFlags flags) {
864 if (0xFF == alpha) {
865 return this->saveLayer(bounds, NULL, flags);
866 } else {
867 SkPaint tmpPaint;
868 tmpPaint.setAlpha(alpha);
869 return this->saveLayer(bounds, &tmpPaint, flags);
870 }
871}
872
873void SkCanvas::restore() {
874 // check for underflow
875 if (fMCStack.count() > 1) {
876 this->internalRestore();
877 }
878}
879
880void SkCanvas::internalRestore() {
881 SkASSERT(fMCStack.count() != 0);
882
883 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000884 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000886 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
887 fClipStack.restore();
888 }
889
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000890 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 DeviceCM* layer = fMCRec->fLayer; // may be null
892 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
893 fMCRec->fLayer = NULL;
894
895 // now do the normal restore()
896 fMCRec->~MCRec(); // balanced in save()
897 fMCStack.pop_back();
898 fMCRec = (MCRec*)fMCStack.back();
899
900 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
901 since if we're being recorded, we don't want to record this (the
902 recorder will have already recorded the restore).
903 */
904 if (NULL != layer) {
905 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000906 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000907 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
908 layer->fPaint);
909 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000911
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000912 SkASSERT(fSaveLayerCount > 0);
913 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 }
915 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000916 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917}
918
919int SkCanvas::getSaveCount() const {
920 return fMCStack.count();
921}
922
923void SkCanvas::restoreToCount(int count) {
924 // sanity check
925 if (count < 1) {
926 count = 1;
927 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000928
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000929 int n = this->getSaveCount() - count;
930 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 this->restore();
932 }
933}
934
reed@google.com7c202932011-12-14 18:48:05 +0000935bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000936 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000937}
938
reed@google.com76f10a32014-02-05 15:32:21 +0000939SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
940 return this->onNewSurface(info);
941}
942
943SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
944 SkBaseDevice* dev = this->getDevice();
945 return dev ? dev->newSurface(info) : NULL;
946}
947
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000948SkImageInfo SkCanvas::imageInfo() const {
949 SkBaseDevice* dev = this->getDevice();
950 if (dev) {
951 return dev->imageInfo();
952 } else {
953 // TODO: need a real unknown for alphatype it seems.
954 SkAlphaType unknownAlphaType = kIgnore_SkAlphaType;
955 return SkImageInfo::Make(0, 0, kUnknown_SkColorType, unknownAlphaType);
956 }
957}
958
959const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
960 return this->onPeekPixels(info, rowBytes);
961}
962
963const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
964 SkBaseDevice* dev = this->getDevice();
965 return dev ? dev->peekPixels(info, rowBytes) : NULL;
966}
967
968SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
969 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
970 if (NULL == fAddr) {
971 fInfo = canvas->imageInfo();
972 if (kUnknown_SkColorType == fInfo.colorType() ||
973 !fBitmap.allocPixels(fInfo))
974 {
975 return; // failure, fAddr is NULL
976 }
977 fBitmap.lockPixels();
978 if (!canvas->readPixels(&fBitmap, 0, 0)) {
979 return; // failure, fAddr is NULL
980 }
981 fAddr = fBitmap.getPixels();
982 fRowBytes = fBitmap.rowBytes();
983 }
984 SkASSERT(fAddr); // success
985}
986
987bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
988 if (fAddr) {
989 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
990 NULL, NULL);
991 } else {
992 bitmap->reset();
993 return false;
994 }
995}
996
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997/////////////////////////////////////////////////////////////////////////////
998
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000999void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001001 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 return;
1003 }
1004
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001005 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001007 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001009
1010 SkDEBUGCODE(bitmap.validate();)
1011 CHECK_LOCKCOUNT_BALANCE(bitmap);
1012
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001013 SkRect storage;
1014 const SkRect* bounds = NULL;
1015 if (paint && paint->canComputeFastBounds()) {
1016 bitmap.getBounds(&storage);
1017 matrix.mapRect(&storage);
1018 bounds = &paint->computeFastBounds(storage, &storage);
1019 }
1020
1021 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001022
1023 while (iter.next()) {
1024 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1025 }
1026
1027 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028}
1029
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001030void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001031 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 SkPaint tmp;
1033 if (NULL == paint) {
1034 tmp.setDither(true);
1035 paint = &tmp;
1036 }
reed@google.com4b226022011-01-11 18:32:13 +00001037
reed@google.com8926b162012-03-23 15:36:36 +00001038 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001040 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001041 paint = &looper.paint();
1042 SkImageFilter* filter = paint->getImageFilter();
1043 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001044 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001045 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001046 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001047 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001048 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001049 SkMatrix matrix = *iter.fMatrix;
1050 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001051 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001052 SkPaint tmpUnfiltered(*paint);
1053 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001054 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1055 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001056 }
1057 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001058 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001059 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001061 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062}
1063
reed@google.com8926b162012-03-23 15:36:36 +00001064void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1065 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001066 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001067 return;
1068 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001069 SkDEBUGCODE(bitmap.validate();)
1070 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001071
reed@google.com8926b162012-03-23 15:36:36 +00001072 SkPaint tmp;
1073 if (NULL == paint) {
1074 paint = &tmp;
1075 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001076
reed@google.com8926b162012-03-23 15:36:36 +00001077 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001078
reed@google.com8926b162012-03-23 15:36:36 +00001079 while (iter.next()) {
1080 paint = &looper.paint();
1081 SkImageFilter* filter = paint->getImageFilter();
1082 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1083 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001084 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001085 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001086 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001087 SkMatrix matrix = *iter.fMatrix;
1088 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001089 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001090 SkPaint tmpUnfiltered(*paint);
1091 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001092 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001093 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001094 }
1095 } else {
1096 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1097 }
1098 }
1099 LOOPER_END
1100}
1101
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102/////////////////////////////////////////////////////////////////////////////
1103
1104bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1105 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001106 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 return fMCRec->fMatrix->preTranslate(dx, dy);
1108}
1109
1110bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1111 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001112 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 return fMCRec->fMatrix->preScale(sx, sy);
1114}
1115
1116bool SkCanvas::rotate(SkScalar degrees) {
1117 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001118 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 return fMCRec->fMatrix->preRotate(degrees);
1120}
1121
1122bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1123 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001124 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 return fMCRec->fMatrix->preSkew(sx, sy);
1126}
1127
1128bool SkCanvas::concat(const SkMatrix& matrix) {
1129 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001130 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 return fMCRec->fMatrix->preConcat(matrix);
1132}
1133
1134void SkCanvas::setMatrix(const SkMatrix& matrix) {
1135 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001136 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 *fMCRec->fMatrix = matrix;
1138}
1139
1140// this is not virtual, so it must call a virtual method so that subclasses
1141// will see its action
1142void SkCanvas::resetMatrix() {
1143 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001144
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 matrix.reset();
1146 this->setMatrix(matrix);
1147}
1148
1149//////////////////////////////////////////////////////////////////////////////
1150
reed@google.comc42d35d2011-10-12 11:57:42 +00001151bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001152#ifdef SK_ENABLE_CLIP_QUICKREJECT
1153 if (SkRegion::kIntersect_Op == op) {
1154 if (fMCRec->fRasterClip->isEmpty()) {
1155 return false;
1156 }
1157
reed@google.com3b3e8952012-08-16 20:53:31 +00001158 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001159 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001160 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001161
1162 fClipStack.clipEmpty();
1163 return fMCRec->fRasterClip->setEmpty();
1164 }
1165 }
1166#endif
1167
reed@google.com5c3d1472011-02-22 19:12:23 +00001168 AutoValidateClip avc(this);
1169
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001171 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001172 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173
1174 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001175 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001176 // the matrix. This means we don't have to a) make a path, and b) tell
1177 // the region code to scan-convert the path, only to discover that it
1178 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180
1181 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001182 fClipStack.clipDevRect(r, op, doAA);
1183 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001185 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001186 // and clip against that, since it can handle any matrix. However, to
1187 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1188 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 SkPath path;
1190
1191 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001192 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 }
1194}
1195
reed@google.com00177082011-10-12 14:34:30 +00001196static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001197 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001198 // base is used to limit the size (and therefore memory allocation) of the
1199 // region that results from scan converting devPath.
1200 SkRegion base;
1201
reed@google.com819c9212011-02-23 18:56:55 +00001202 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001203 // since we are intersect, we can do better (tighter) with currRgn's
1204 // bounds, than just using the device. However, if currRgn is complex,
1205 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001206 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001207 // FIXME: we should also be able to do this when currClip->isBW(),
1208 // but relaxing the test above triggers GM asserts in
1209 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1210 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001211 } else {
reed@google.com00177082011-10-12 14:34:30 +00001212 base.setRect(currClip->getBounds());
1213 SkRasterClip clip;
1214 clip.setPath(devPath, base, doAA);
1215 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001216 }
reed@google.com819c9212011-02-23 18:56:55 +00001217 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001218 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001219 if (!device) {
1220 return currClip->setEmpty();
1221 }
1222
junov@chromium.orga907ac32012-02-24 21:54:07 +00001223 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001224
1225 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001226 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001227 } else {
reed@google.com00177082011-10-12 14:34:30 +00001228 SkRasterClip clip;
1229 clip.setPath(devPath, base, doAA);
1230 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001231 }
1232 }
1233}
1234
reed@google.com4ed0fb72012-12-12 20:48:18 +00001235bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1236 if (rrect.isRect()) {
1237 // call the non-virtual version
1238 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001239 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001240
1241 SkRRect transformedRRect;
1242 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1243 AutoValidateClip avc(this);
1244
1245 fDeviceCMDirty = true;
1246 fCachedLocalClipBoundsDirty = true;
1247 doAA &= fAllowSoftClip;
1248
1249 fClipStack.clipDevRRect(transformedRRect, op, doAA);
1250
1251 SkPath devPath;
1252 devPath.addRRect(transformedRRect);
1253
1254 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
1255 }
1256
1257 SkPath path;
1258 path.addRRect(rrect);
1259 // call the non-virtual version
1260 return this->SkCanvas::clipPath(path, op, doAA);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001261}
1262
reed@google.comc42d35d2011-10-12 11:57:42 +00001263bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001264#ifdef SK_ENABLE_CLIP_QUICKREJECT
1265 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1266 if (fMCRec->fRasterClip->isEmpty()) {
1267 return false;
1268 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001269
reed@google.com3b3e8952012-08-16 20:53:31 +00001270 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001271 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001272 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001273
reed@google.comda17f752012-08-16 18:27:05 +00001274 fClipStack.clipEmpty();
1275 return fMCRec->fRasterClip->setEmpty();
1276 }
1277 }
1278#endif
1279
reed@google.com5c3d1472011-02-22 19:12:23 +00001280 AutoValidateClip avc(this);
1281
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001283 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001284 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285
1286 SkPath devPath;
1287 path.transform(*fMCRec->fMatrix, &devPath);
1288
reed@google.comfe701122011-11-08 19:41:23 +00001289 // Check if the transfomation, or the original path itself
1290 // made us empty. Note this can also happen if we contained NaN
1291 // values. computing the bounds detects this, and will set our
1292 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1293 if (devPath.getBounds().isEmpty()) {
1294 // resetting the path will remove any NaN or other wanky values
1295 // that might upset our scan converter.
1296 devPath.reset();
1297 }
1298
reed@google.com5c3d1472011-02-22 19:12:23 +00001299 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001300 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001301
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001302 if (fAllowSimplifyClip) {
1303 devPath.reset();
1304 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1305 const SkClipStack* clipStack = getClipStack();
1306 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1307 const SkClipStack::Element* element;
1308 while ((element = iter.next())) {
1309 SkClipStack::Element::Type type = element->getType();
1310 if (type == SkClipStack::Element::kEmpty_Type) {
1311 continue;
1312 }
1313 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001314 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001315 SkRegion::Op elementOp = element->getOp();
1316 if (elementOp == SkRegion::kReplace_Op) {
1317 devPath = operand;
1318 } else {
1319 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1320 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001321 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1322 // perhaps we need an API change to avoid this sort of mixed-signals about
1323 // clipping.
1324 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001325 }
1326 op = SkRegion::kReplace_Op;
1327 }
1328
reed@google.com00177082011-10-12 14:34:30 +00001329 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330}
1331
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001332bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1333 bool inverseFilled) {
1334 // This is for updating the clip conservatively using only bounds
1335 // information.
1336 // Contract:
1337 // The current clip must contain the true clip. The true
1338 // clip is the clip that would have normally been computed
1339 // by calls to clipPath and clipRRect
1340 // Objective:
1341 // Keep the current clip as small as possible without
1342 // breaking the contract, using only clip bounding rectangles
1343 // (for performance).
1344
1345 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1346 // don't have to worry about getting caught in a loop. Thus anywhere
1347 // we call a virtual method, we explicitly prefix it with
1348 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001349
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001350 if (inverseFilled) {
1351 switch (op) {
1352 case SkRegion::kIntersect_Op:
1353 case SkRegion::kDifference_Op:
1354 // These ops can only shrink the current clip. So leaving
1355 // the clip unchanges conservatively respects the contract.
1356 return this->getClipDeviceBounds(NULL);
1357 case SkRegion::kUnion_Op:
1358 case SkRegion::kReplace_Op:
1359 case SkRegion::kReverseDifference_Op:
1360 case SkRegion::kXOR_Op:
1361 {
1362 // These ops can grow the current clip up to the extents of
1363 // the input clip, which is inverse filled, so we just set
1364 // the current clip to the device bounds.
1365 SkRect deviceBounds;
1366 SkIRect deviceIBounds;
1367 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001368 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001369 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1370 // set the clip in device space
1371 this->SkCanvas::setMatrix(SkMatrix::I());
1372 bool result = this->SkCanvas::clipRect(deviceBounds,
1373 SkRegion::kReplace_Op, false);
1374 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1375 return result;
1376 }
1377 default:
1378 SkASSERT(0); // unhandled op?
1379 }
1380 } else {
1381 // Not inverse filled
1382 switch (op) {
1383 case SkRegion::kIntersect_Op:
1384 case SkRegion::kUnion_Op:
1385 case SkRegion::kReplace_Op:
1386 return this->SkCanvas::clipRect(bounds, op, false);
1387 case SkRegion::kDifference_Op:
1388 // Difference can only shrink the current clip.
1389 // Leaving clip unchanged conservatively fullfills the contract.
1390 return this->getClipDeviceBounds(NULL);
1391 case SkRegion::kReverseDifference_Op:
1392 // To reverse, we swap in the bounds with a replace op.
1393 // As with difference, leave it unchanged.
1394 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1395 case SkRegion::kXOR_Op:
1396 // Be conservative, based on (A XOR B) always included in (A union B),
1397 // which is always included in (bounds(A) union bounds(B))
1398 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1399 default:
1400 SkASSERT(0); // unhandled op?
1401 }
1402 }
1403 return true;
1404}
1405
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001407 AutoValidateClip avc(this);
1408
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001410 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411
reed@google.com5c3d1472011-02-22 19:12:23 +00001412 // todo: signal fClipStack that we have a region, and therefore (I guess)
1413 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001414 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001415
reed@google.com00177082011-10-12 14:34:30 +00001416 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417}
1418
reed@google.com819c9212011-02-23 18:56:55 +00001419#ifdef SK_DEBUG
1420void SkCanvas::validateClip() const {
1421 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001422 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001423 if (!device) {
1424 SkASSERT(this->getTotalClip().isEmpty());
1425 return;
1426 }
1427
reed@google.com819c9212011-02-23 18:56:55 +00001428 SkIRect ir;
1429 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001430 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001431
robertphillips@google.com80214e22012-07-20 15:33:18 +00001432 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001433 const SkClipStack::Element* element;
1434 while ((element = iter.next()) != NULL) {
1435 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001436 case SkClipStack::Element::kRect_Type:
1437 element->getRect().round(&ir);
1438 tmpClip.op(ir, element->getOp());
1439 break;
1440 case SkClipStack::Element::kEmpty_Type:
1441 tmpClip.setEmpty();
1442 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001443 default: {
1444 SkPath path;
1445 element->asPath(&path);
1446 clipPathHelper(this,
1447 &tmpClip,
1448 path,
1449 element->getOp(),
1450 element->isAA());
1451 break;
1452 }
reed@google.com819c9212011-02-23 18:56:55 +00001453 }
1454 }
1455
reed@google.com6f8f2922011-03-04 22:27:10 +00001456#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001457 // now compare against the current rgn
1458 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001459 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001460#endif
reed@google.com819c9212011-02-23 18:56:55 +00001461}
1462#endif
1463
reed@google.com90c07ea2012-04-13 13:50:27 +00001464void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001465 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001466 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001467
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001468 static const SkRect kEmpty = { 0, 0, 0, 0 };
1469 while ((element = iter.next()) != NULL) {
1470 switch (element->getType()) {
1471 case SkClipStack::Element::kPath_Type:
1472 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1473 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001474 case SkClipStack::Element::kRRect_Type:
1475 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1476 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001477 case SkClipStack::Element::kRect_Type:
1478 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1479 break;
1480 case SkClipStack::Element::kEmpty_Type:
1481 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1482 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001483 }
1484 }
1485}
1486
reed@google.com5c3d1472011-02-22 19:12:23 +00001487///////////////////////////////////////////////////////////////////////////////
1488
reed@google.com3b3e8952012-08-16 20:53:31 +00001489bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001490
reed@google.com16078632011-12-06 18:56:37 +00001491 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001492 return true;
1493
reed@google.com00177082011-10-12 14:34:30 +00001494 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495 return true;
1496 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
tomhudson@google.com8d430182011-06-06 19:11:19 +00001498 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001499 SkRect dst;
1500 fMCRec->fMatrix->mapRect(&dst, rect);
1501 SkIRect idst;
1502 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001503 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001504 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001505 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001506
reed@android.coma380ae42009-07-21 01:17:02 +00001507 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001508 // TODO: should we use | instead, or compare all 4 at once?
1509 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001510 return true;
1511 }
reed@google.comc0784db2013-12-13 21:16:12 +00001512 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001513 return true;
1514 }
1515 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517}
1518
reed@google.com3b3e8952012-08-16 20:53:31 +00001519bool SkCanvas::quickReject(const SkPath& path) const {
1520 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521}
1522
reed@google.com3b3e8952012-08-16 20:53:31 +00001523bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001524 SkIRect ibounds;
1525 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526 return false;
1527 }
1528
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001529 SkMatrix inverse;
1530 // if we can't invert the CTM, we can't return local clip bounds
1531 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001532 if (bounds) {
1533 bounds->setEmpty();
1534 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001535 return false;
1536 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001538 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001539 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001540 // adjust it outwards in case we are antialiasing
1541 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001542
reed@google.com8f4d2302013-12-17 16:44:46 +00001543 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1544 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545 inverse.mapRect(bounds, r);
1546 }
1547 return true;
1548}
1549
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001550bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001551 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001552 if (clip.isEmpty()) {
1553 if (bounds) {
1554 bounds->setEmpty();
1555 }
1556 return false;
1557 }
1558
1559 if (NULL != bounds) {
1560 *bounds = clip.getBounds();
1561 }
1562 return true;
1563}
1564
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565const SkMatrix& SkCanvas::getTotalMatrix() const {
1566 return *fMCRec->fMatrix;
1567}
1568
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001569SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001570 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1571 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001572 return kComplex_ClipType;
1573}
1574
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001576 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577}
1578
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001579SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001580 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001581 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582}
1583
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001584GrContext* SkCanvas::getGrContext() {
1585#if SK_SUPPORT_GPU
1586 SkBaseDevice* device = this->getTopDevice();
1587 if (NULL != device) {
1588 GrRenderTarget* renderTarget = device->accessRenderTarget();
1589 if (NULL != renderTarget) {
1590 return renderTarget->getContext();
1591 }
1592 }
1593#endif
1594
1595 return NULL;
1596
1597}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001598
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599//////////////////////////////////////////////////////////////////////////////
1600// These are the virtual drawing methods
1601//////////////////////////////////////////////////////////////////////////////
1602
reed@google.com2a981812011-04-14 18:59:28 +00001603void SkCanvas::clear(SkColor color) {
1604 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001605 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001606 while (iter.next()) {
1607 iter.fDevice->clear(color);
1608 }
1609}
1610
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001612 this->internalDrawPaint(paint);
1613}
1614
1615void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001616 CHECK_SHADER_NOSETCONTEXT(paint);
1617
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001618 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619
1620 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001621 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622 }
1623
reed@google.com4e2b3d32011-04-07 14:18:59 +00001624 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625}
1626
1627void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1628 const SkPaint& paint) {
1629 if ((long)count <= 0) {
1630 return;
1631 }
1632
reed@google.comea033602012-12-14 13:13:55 +00001633 CHECK_SHADER_NOSETCONTEXT(paint);
1634
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001635 SkRect r, storage;
1636 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001637 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001638 // special-case 2 points (common for drawing a single line)
1639 if (2 == count) {
1640 r.set(pts[0], pts[1]);
1641 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001642 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001643 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001644 bounds = &paint.computeFastStrokeBounds(r, &storage);
1645 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001646 return;
1647 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001648 }
reed@google.coma584aed2012-05-16 14:06:02 +00001649
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650 SkASSERT(pts != NULL);
1651
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001652 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001653
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001655 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656 }
reed@google.com4b226022011-01-11 18:32:13 +00001657
reed@google.com4e2b3d32011-04-07 14:18:59 +00001658 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001659}
1660
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001661void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001662 CHECK_SHADER_NOSETCONTEXT(paint);
1663
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001664 SkRect storage;
1665 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001666 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001667 bounds = &paint.computeFastBounds(r, &storage);
1668 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669 return;
1670 }
1671 }
reed@google.com4b226022011-01-11 18:32:13 +00001672
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001673 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674
1675 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001676 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677 }
1678
reed@google.com4e2b3d32011-04-07 14:18:59 +00001679 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680}
1681
reed@google.com4ed0fb72012-12-12 20:48:18 +00001682void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001683 CHECK_SHADER_NOSETCONTEXT(paint);
1684
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001685 SkRect storage;
1686 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001687 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001688 bounds = &paint.computeFastBounds(oval, &storage);
1689 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001690 return;
1691 }
1692 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001693
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001694 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001695
1696 while (iter.next()) {
1697 iter.fDevice->drawOval(iter, oval, looper.paint());
1698 }
1699
1700 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001701}
1702
1703void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001704 CHECK_SHADER_NOSETCONTEXT(paint);
1705
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001706 SkRect storage;
1707 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001708 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001709 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1710 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001711 return;
1712 }
1713 }
1714
1715 if (rrect.isRect()) {
1716 // call the non-virtual version
1717 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001718 return;
1719 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001720 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001721 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1722 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001723 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001724
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001725 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001726
1727 while (iter.next()) {
1728 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1729 }
1730
1731 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001732}
1733
1734
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001735void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001736 CHECK_SHADER_NOSETCONTEXT(paint);
1737
reed@google.com93645112012-07-26 16:11:47 +00001738 if (!path.isFinite()) {
1739 return;
1740 }
1741
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001742 SkRect storage;
1743 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001744 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001745 const SkRect& pathBounds = path.getBounds();
1746 bounds = &paint.computeFastBounds(pathBounds, &storage);
1747 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 return;
1749 }
1750 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001751 if (path.isEmpty()) {
1752 if (path.isInverseFillType()) {
1753 this->internalDrawPaint(paint);
1754 }
1755 return;
1756 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001758 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759
1760 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001761 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 }
1763
reed@google.com4e2b3d32011-04-07 14:18:59 +00001764 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
1767void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1768 const SkPaint* paint) {
1769 SkDEBUGCODE(bitmap.validate();)
1770
reed@google.com3d608122011-11-21 15:16:16 +00001771 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001772 SkRect bounds = {
1773 x, y,
1774 x + SkIntToScalar(bitmap.width()),
1775 y + SkIntToScalar(bitmap.height())
1776 };
1777 if (paint) {
1778 (void)paint->computeFastBounds(bounds, &bounds);
1779 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001780 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 return;
1782 }
1783 }
reed@google.com4b226022011-01-11 18:32:13 +00001784
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 SkMatrix matrix;
1786 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001787 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788}
1789
reed@google.com9987ec32011-09-07 11:57:52 +00001790// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001791void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001792 const SkRect& dst, const SkPaint* paint,
1793 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001794 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 return;
1796 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001797
reed@google.comea033602012-12-14 13:13:55 +00001798 CHECK_LOCKCOUNT_BALANCE(bitmap);
1799
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001800 SkRect storage;
1801 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001802 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001803 if (paint) {
1804 bounds = &paint->computeFastBounds(dst, &storage);
1805 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001806 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001807 return;
1808 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809 }
reed@google.com3d608122011-11-21 15:16:16 +00001810
reed@google.com33535f32012-09-25 15:37:50 +00001811 SkLazyPaint lazy;
1812 if (NULL == paint) {
1813 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001815
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001816 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001817
reed@google.com33535f32012-09-25 15:37:50 +00001818 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001819 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001820 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001821
reed@google.com33535f32012-09-25 15:37:50 +00001822 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823}
1824
reed@google.com71121732012-09-18 15:14:33 +00001825void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001826 const SkRect& dst, const SkPaint* paint,
1827 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001828 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001829 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001830}
1831
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1833 const SkPaint* paint) {
1834 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001835 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836}
1837
reed@google.com9987ec32011-09-07 11:57:52 +00001838void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1839 const SkIRect& center, const SkRect& dst,
1840 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001841 if (bitmap.drawsNothing()) {
1842 return;
1843 }
reed@google.com3d608122011-11-21 15:16:16 +00001844 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001845 SkRect storage;
1846 const SkRect* bounds = &dst;
1847 if (paint) {
1848 bounds = &paint->computeFastBounds(dst, &storage);
1849 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001850 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001851 return;
1852 }
1853 }
1854
reed@google.com9987ec32011-09-07 11:57:52 +00001855 const int32_t w = bitmap.width();
1856 const int32_t h = bitmap.height();
1857
1858 SkIRect c = center;
1859 // pin center to the bounds of the bitmap
1860 c.fLeft = SkMax32(0, center.fLeft);
1861 c.fTop = SkMax32(0, center.fTop);
1862 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1863 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1864
reed@google.com71121732012-09-18 15:14:33 +00001865 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001866 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001867 };
1868 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001869 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001870 };
reed@google.com9987ec32011-09-07 11:57:52 +00001871 SkScalar dstX[4] = {
1872 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1873 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1874 };
1875 SkScalar dstY[4] = {
1876 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1877 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1878 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001879
reed@google.com9987ec32011-09-07 11:57:52 +00001880 if (dstX[1] > dstX[2]) {
1881 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1882 dstX[2] = dstX[1];
1883 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001884
reed@google.com9987ec32011-09-07 11:57:52 +00001885 if (dstY[1] > dstY[2]) {
1886 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1887 dstY[2] = dstY[1];
1888 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001889
reed@google.com9987ec32011-09-07 11:57:52 +00001890 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001891 SkRect s, d;
1892
reed@google.com9987ec32011-09-07 11:57:52 +00001893 s.fTop = srcY[y];
1894 s.fBottom = srcY[y+1];
1895 d.fTop = dstY[y];
1896 d.fBottom = dstY[y+1];
1897 for (int x = 0; x < 3; x++) {
1898 s.fLeft = srcX[x];
1899 s.fRight = srcX[x+1];
1900 d.fLeft = dstX[x];
1901 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001902 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001903 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001904 }
1905 }
1906}
1907
1908void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1909 const SkRect& dst, const SkPaint* paint) {
1910 SkDEBUGCODE(bitmap.validate();)
1911
1912 // Need a device entry-point, so gpu can use a mesh
1913 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1914}
1915
reed@google.comf67e4cf2011-03-15 20:56:58 +00001916class SkDeviceFilteredPaint {
1917public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001918 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1919 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001920 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001921 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001922 newPaint->setFlags(flags.fFlags);
1923 newPaint->setHinting(flags.fHinting);
1924 fPaint = newPaint;
1925 } else {
1926 fPaint = &paint;
1927 }
1928 }
1929
reed@google.comf67e4cf2011-03-15 20:56:58 +00001930 const SkPaint& paint() const { return *fPaint; }
1931
1932private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001933 const SkPaint* fPaint;
1934 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001935};
1936
bungeman@google.com52c748b2011-08-22 21:30:43 +00001937void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1938 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001939 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001940 draw.fDevice->drawRect(draw, r, paint);
1941 } else {
1942 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001943 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001944 draw.fDevice->drawRect(draw, r, p);
1945 }
1946}
1947
1948void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1949 const char text[], size_t byteLength,
1950 SkScalar x, SkScalar y) {
1951 SkASSERT(byteLength == 0 || text != NULL);
1952
1953 // nothing to draw
1954 if (text == NULL || byteLength == 0 ||
1955 draw.fClip->isEmpty() ||
1956 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1957 return;
1958 }
1959
1960 SkScalar width = 0;
1961 SkPoint start;
1962
1963 start.set(0, 0); // to avoid warning
1964 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1965 SkPaint::kStrikeThruText_Flag)) {
1966 width = paint.measureText(text, byteLength);
1967
1968 SkScalar offsetX = 0;
1969 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1970 offsetX = SkScalarHalf(width);
1971 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1972 offsetX = width;
1973 }
1974 start.set(x - offsetX, y);
1975 }
1976
1977 if (0 == width) {
1978 return;
1979 }
1980
1981 uint32_t flags = paint.getFlags();
1982
1983 if (flags & (SkPaint::kUnderlineText_Flag |
1984 SkPaint::kStrikeThruText_Flag)) {
1985 SkScalar textSize = paint.getTextSize();
1986 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1987 SkRect r;
1988
1989 r.fLeft = start.fX;
1990 r.fRight = start.fX + width;
1991
1992 if (flags & SkPaint::kUnderlineText_Flag) {
1993 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1994 start.fY);
1995 r.fTop = offset;
1996 r.fBottom = offset + height;
1997 DrawRect(draw, paint, r, textSize);
1998 }
1999 if (flags & SkPaint::kStrikeThruText_Flag) {
2000 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2001 start.fY);
2002 r.fTop = offset;
2003 r.fBottom = offset + height;
2004 DrawRect(draw, paint, r, textSize);
2005 }
2006 }
2007}
2008
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009void SkCanvas::drawText(const void* text, size_t byteLength,
2010 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002011 CHECK_SHADER_NOSETCONTEXT(paint);
2012
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002013 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014
2015 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002016 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002017 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002018 DrawTextDecorations(iter, dfp.paint(),
2019 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002020 }
2021
reed@google.com4e2b3d32011-04-07 14:18:59 +00002022 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002023}
2024
2025void SkCanvas::drawPosText(const void* text, size_t byteLength,
2026 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002027 CHECK_SHADER_NOSETCONTEXT(paint);
2028
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002029 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002030
reed@android.com8a1c16f2008-12-17 15:59:43 +00002031 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002032 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002034 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 }
reed@google.com4b226022011-01-11 18:32:13 +00002036
reed@google.com4e2b3d32011-04-07 14:18:59 +00002037 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038}
2039
2040void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2041 const SkScalar xpos[], SkScalar constY,
2042 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002043 CHECK_SHADER_NOSETCONTEXT(paint);
2044
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002045 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002046
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002048 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002049 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002050 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051 }
reed@google.com4b226022011-01-11 18:32:13 +00002052
reed@google.com4e2b3d32011-04-07 14:18:59 +00002053 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054}
2055
2056void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2057 const SkPath& path, const SkMatrix* matrix,
2058 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002059 CHECK_SHADER_NOSETCONTEXT(paint);
2060
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002061 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062
2063 while (iter.next()) {
2064 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002065 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066 }
2067
reed@google.com4e2b3d32011-04-07 14:18:59 +00002068 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069}
2070
2071void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2072 const SkPoint verts[], const SkPoint texs[],
2073 const SkColor colors[], SkXfermode* xmode,
2074 const uint16_t indices[], int indexCount,
2075 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002076 CHECK_SHADER_NOSETCONTEXT(paint);
2077
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002078 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002079
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080 while (iter.next()) {
2081 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002082 colors, xmode, indices, indexCount,
2083 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084 }
reed@google.com4b226022011-01-11 18:32:13 +00002085
reed@google.com4e2b3d32011-04-07 14:18:59 +00002086 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002087}
2088
2089//////////////////////////////////////////////////////////////////////////////
2090// These methods are NOT virtual, and therefore must call back into virtual
2091// methods, rather than actually drawing themselves.
2092//////////////////////////////////////////////////////////////////////////////
2093
2094void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002095 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096 SkPaint paint;
2097
2098 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002099 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002100 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 }
2102 this->drawPaint(paint);
2103}
2104
reed@android.com845fdac2009-06-23 03:01:32 +00002105void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106 SkPaint paint;
2107
2108 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002109 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002110 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 }
2112 this->drawPaint(paint);
2113}
2114
2115void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2116 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002117
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118 pt.set(x, y);
2119 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2120}
2121
2122void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2123 SkPoint pt;
2124 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002125
reed@android.com8a1c16f2008-12-17 15:59:43 +00002126 pt.set(x, y);
2127 paint.setColor(color);
2128 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2129}
2130
2131void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2132 const SkPaint& paint) {
2133 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002134
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135 pts[0].set(x0, y0);
2136 pts[1].set(x1, y1);
2137 this->drawPoints(kLines_PointMode, 2, pts, paint);
2138}
2139
2140void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2141 SkScalar right, SkScalar bottom,
2142 const SkPaint& paint) {
2143 SkRect r;
2144
2145 r.set(left, top, right, bottom);
2146 this->drawRect(r, paint);
2147}
2148
2149void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2150 const SkPaint& paint) {
2151 if (radius < 0) {
2152 radius = 0;
2153 }
2154
2155 SkRect r;
2156 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002157 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158}
2159
2160void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2161 const SkPaint& paint) {
2162 if (rx > 0 && ry > 0) {
2163 if (paint.canComputeFastBounds()) {
2164 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002165 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166 return;
2167 }
2168 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002169 SkRRect rrect;
2170 rrect.setRectXY(r, rx, ry);
2171 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 } else {
2173 this->drawRect(r, paint);
2174 }
2175}
2176
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2178 SkScalar sweepAngle, bool useCenter,
2179 const SkPaint& paint) {
2180 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2181 this->drawOval(oval, paint);
2182 } else {
2183 SkPath path;
2184 if (useCenter) {
2185 path.moveTo(oval.centerX(), oval.centerY());
2186 }
2187 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2188 if (useCenter) {
2189 path.close();
2190 }
2191 this->drawPath(path, paint);
2192 }
2193}
2194
2195void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2196 const SkPath& path, SkScalar hOffset,
2197 SkScalar vOffset, const SkPaint& paint) {
2198 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002199
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 matrix.setTranslate(hOffset, vOffset);
2201 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2202}
2203
reed@android.comf76bacf2009-05-13 14:00:33 +00002204///////////////////////////////////////////////////////////////////////////////
2205
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208}
2209
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210///////////////////////////////////////////////////////////////////////////////
2211///////////////////////////////////////////////////////////////////////////////
2212
2213SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002214 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215
2216 SkASSERT(canvas);
2217
2218 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2219 fDone = !fImpl->next();
2220}
2221
2222SkCanvas::LayerIter::~LayerIter() {
2223 fImpl->~SkDrawIter();
2224}
2225
2226void SkCanvas::LayerIter::next() {
2227 fDone = !fImpl->next();
2228}
2229
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002230SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231 return fImpl->getDevice();
2232}
2233
2234const SkMatrix& SkCanvas::LayerIter::matrix() const {
2235 return fImpl->getMatrix();
2236}
2237
2238const SkPaint& SkCanvas::LayerIter::paint() const {
2239 const SkPaint* paint = fImpl->getPaint();
2240 if (NULL == paint) {
2241 paint = &fDefaultPaint;
2242 }
2243 return *paint;
2244}
2245
2246const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2247int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2248int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002249
2250///////////////////////////////////////////////////////////////////////////////
2251
2252SkCanvas::ClipVisitor::~ClipVisitor() { }