blob: ed8274dc5d3b4a9bf09d6241541620b1a11f9a4f [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkBounder.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
reed@google.com97af1a62012-08-28 12:19:02 +000022#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000024#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000025#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000028#if SK_SUPPORT_GPU
29#include "GrRenderTarget.h"
30#endif
31
reed@google.comda17f752012-08-16 18:27:05 +000032// experimental for faster tiled drawing...
33//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000034
reed@android.com8a1c16f2008-12-17 15:59:43 +000035//#define SK_TRACE_SAVERESTORE
36
37#ifdef SK_TRACE_SAVERESTORE
38 static int gLayerCounter;
39 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
40 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
41
42 static int gRecCounter;
43 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
44 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
45
46 static int gCanvasCounter;
47 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
48 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
49#else
50 #define inc_layer()
51 #define dec_layer()
52 #define inc_rec()
53 #define dec_rec()
54 #define inc_canvas()
55 #define dec_canvas()
56#endif
57
reed@google.comea033602012-12-14 13:13:55 +000058#ifdef SK_DEBUG
59#include "SkPixelRef.h"
60
reed@google.comf53d0a92013-01-30 13:17:32 +000061/*
62 * Some pixelref subclasses can support being "locked" from another thread
63 * during the lock-scope of skia calling them. In these instances, this balance
64 * check will fail, but may not be indicative of a problem, so we allow a build
65 * flag to disable this check.
66 *
67 * Potentially another fix would be to have a (debug-only) virtual or flag on
68 * pixelref, which could tell us at runtime if this check is valid. That would
69 * eliminate the need for this heavy-handed build check.
70 */
71#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
72class AutoCheckLockCountBalance {
73public:
74 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
75};
76#else
reed@google.comea033602012-12-14 13:13:55 +000077class AutoCheckLockCountBalance {
78public:
79 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
80 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
81 }
82 ~AutoCheckLockCountBalance() {
83 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
84 SkASSERT(count == fLockCount);
85 }
86
87private:
88 const SkPixelRef* fPixelRef;
89 int fLockCount;
90};
reed@google.comf53d0a92013-01-30 13:17:32 +000091#endif
reed@google.comea033602012-12-14 13:13:55 +000092
93class AutoCheckNoSetContext {
94public:
95 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
96 this->assertNoSetContext(fPaint);
97 }
98 ~AutoCheckNoSetContext() {
99 this->assertNoSetContext(fPaint);
100 }
101
102private:
103 const SkPaint& fPaint;
104
105 void assertNoSetContext(const SkPaint& paint) {
106 SkShader* s = paint.getShader();
107 if (s) {
108 SkASSERT(!s->setContextHasBeenCalled());
109 }
110 }
111};
112
113#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
114#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
115
116#else
117 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
118 #define CHECK_SHADER_NOSETCONTEXT(paint)
119#endif
120
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000121typedef SkTLazy<SkPaint> SkLazyPaint;
122
reed@google.com97af1a62012-08-28 12:19:02 +0000123void SkCanvas::predrawNotify() {
124 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000125 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000126 }
127}
128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000131/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 The clip/matrix/proc are fields that reflect the top of the save/restore
133 stack. Whenever the canvas changes, it marks a dirty flag, and then before
134 these are used (assuming we're not on a layer) we rebuild these cache
135 values: they reflect the top of the save stack, but translated and clipped
136 by the device's XY offset and bitmap-bounds.
137*/
138struct DeviceCM {
139 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000140 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000141 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000143 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000145 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 : fNext(NULL) {
147 if (NULL != device) {
148 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000149 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 }
reed@google.com4b226022011-01-11 18:32:13 +0000151 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000153 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000155 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000157 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 fDevice->unref();
159 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000160 SkDELETE(fPaint);
161 }
reed@google.com4b226022011-01-11 18:32:13 +0000162
reed@google.com045e62d2011-10-24 12:19:46 +0000163 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
164 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000165 int x = fDevice->getOrigin().x();
166 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 int width = fDevice->width();
168 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 if ((x | y) == 0) {
171 fMatrix = &totalMatrix;
172 fClip = totalClip;
173 } else {
174 fMatrixStorage = totalMatrix;
175 fMatrixStorage.postTranslate(SkIntToScalar(-x),
176 SkIntToScalar(-y));
177 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 totalClip.translate(-x, -y, &fClip);
180 }
181
reed@google.com045e62d2011-10-24 12:19:46 +0000182 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
184 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000187 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 SkRegion::kDifference_Op);
189 }
reed@google.com4b226022011-01-11 18:32:13 +0000190
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000191 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193#ifdef SK_DEBUG
194 if (!fClip.isEmpty()) {
195 SkIRect deviceR;
196 deviceR.set(0, 0, width, height);
197 SkASSERT(deviceR.contains(fClip.getBounds()));
198 }
199#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000200 }
201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000203 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204};
205
206/* This is the record we keep for each save/restore level in the stack.
207 Since a level optionally copies the matrix and/or stack, we have pointers
208 for these fields. If the value is copied for this level, the copy is
209 stored in the ...Storage field, and the pointer points to that. If the
210 value is not copied for this level, we ignore ...Storage, and just point
211 at the corresponding value in the previous level in the stack.
212*/
213class SkCanvas::MCRec {
214public:
215 MCRec* fNext;
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000216 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
219 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 DeviceCM* fLayer;
222 /* If there are any layers in the stack, this points to the top-most
223 one that is at or below this level in the stack (so we know what
224 bitmap/device to draw into from this level. This value is NOT
225 reference counted, since the real owner is either our fLayer field,
226 or a previous one in a lower level.)
227 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000228 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000230 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if (NULL != prev) {
232 if (flags & SkCanvas::kMatrix_SaveFlag) {
233 fMatrixStorage = *prev->fMatrix;
234 fMatrix = &fMatrixStorage;
235 } else {
236 fMatrix = prev->fMatrix;
237 }
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000240 fRasterClipStorage = *prev->fRasterClip;
241 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 } else {
reed@google.com00177082011-10-12 14:34:30 +0000243 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
245
246 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000247 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 fTopLayer = prev->fTopLayer;
250 } else { // no prev
251 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000254 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = NULL;
256 fTopLayer = NULL;
257 }
258 fLayer = NULL;
259
260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000264 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDELETE(fLayer);
266 dec_rec();
267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
reed@google.com00177082011-10-12 14:34:30 +0000270 SkMatrix fMatrixStorage;
271 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
274class SkDrawIter : public SkDraw {
275public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000277 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000278 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 canvas->updateDeviceCMCache();
280
reed@google.com90c07ea2012-04-13 13:50:27 +0000281 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fBounder = canvas->getBounder();
283 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000284 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com4b226022011-01-11 18:32:13 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 bool next() {
288 // skip over recs with empty clips
289 if (fSkipEmptyClips) {
290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
291 fCurrLayer = fCurrLayer->fNext;
292 }
293 }
294
reed@google.comf68c5e22012-02-24 16:38:58 +0000295 const DeviceCM* rec = fCurrLayer;
296 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297
298 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
300 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 fDevice = rec->fDevice;
302 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000304 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 fCurrLayer = rec->fNext;
307 if (fBounder) {
308 fBounder->setClip(fClip);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313 }
314 return false;
315 }
reed@google.com4b226022011-01-11 18:32:13 +0000316
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000317 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000318 int getX() const { return fDevice->getOrigin().x(); }
319 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 const SkMatrix& getMatrix() const { return *fMatrix; }
321 const SkRegion& getClip() const { return *fClip; }
322 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324private:
325 SkCanvas* fCanvas;
326 const DeviceCM* fCurrLayer;
327 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 SkBool8 fSkipEmptyClips;
329
330 typedef SkDraw INHERITED;
331};
332
333/////////////////////////////////////////////////////////////////////////////
334
335class AutoDrawLooper {
336public:
reed@google.com8926b162012-03-23 15:36:36 +0000337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000338 bool skipLayerForImageFilter = false,
339 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fCanvas = canvas;
341 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000343 fPaint = NULL;
344 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000345 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000346 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
reed@google.com8926b162012-03-23 15:36:36 +0000348 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
349 SkPaint tmp;
350 tmp.setImageFilter(fOrigPaint.getImageFilter());
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000351 (void)canvas->internalSaveLayer(bounds, &tmp,
reed@google.com8926b162012-03-23 15:36:36 +0000352 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
353 // we'll clear the imageFilter for the actual draws in next(), so
354 // it will only be applied during the restore().
355 fDoClearImageFilter = true;
356 }
357
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 if (fLooper) {
359 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000360 fIsSimple = false;
361 } else {
362 // can we be marked as simple?
363 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364 }
365 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000368 if (fDoClearImageFilter) {
369 fCanvas->internalRestore();
370 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000371 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000373
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 const SkPaint& paint() const {
375 SkASSERT(fPaint);
376 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000378
reed@google.com129ec222012-05-15 13:24:09 +0000379 bool next(SkDrawFilter::Type drawType) {
380 if (fDone) {
381 return false;
382 } else if (fIsSimple) {
383 fDone = true;
384 fPaint = &fOrigPaint;
385 return !fPaint->nothingToDraw();
386 } else {
387 return this->doNext(drawType);
388 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000389 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000392 SkLazyPaint fLazyPaint;
393 SkCanvas* fCanvas;
394 const SkPaint& fOrigPaint;
395 SkDrawLooper* fLooper;
396 SkDrawFilter* fFilter;
397 const SkPaint* fPaint;
398 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000399 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000400 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000401 bool fIsSimple;
402
403 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404};
405
reed@google.com129ec222012-05-15 13:24:09 +0000406bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000407 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000408 SkASSERT(!fIsSimple);
409 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
410
411 SkPaint* paint = fLazyPaint.set(fOrigPaint);
412
413 if (fDoClearImageFilter) {
414 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416
reed@google.com129ec222012-05-15 13:24:09 +0000417 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000419 return false;
420 }
421 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000422 if (!fFilter->filter(paint, drawType)) {
423 fDone = true;
424 return false;
425 }
reed@google.com129ec222012-05-15 13:24:09 +0000426 if (NULL == fLooper) {
427 // no looper means we only draw once
428 fDone = true;
429 }
430 }
431 fPaint = paint;
432
433 // if we only came in here for the imagefilter, mark us as done
434 if (!fLooper && !fFilter) {
435 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000436 }
437
438 // call this after any possible paint modifiers
439 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440 fPaint = NULL;
441 return false;
442 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 return true;
444}
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446/* Stack helper for managing a SkBounder. In the destructor, if we were
447 given a bounder, we call its commit() method, signifying that we are
448 done accumulating bounds for that draw.
449*/
450class SkAutoBounderCommit {
451public:
452 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
453 ~SkAutoBounderCommit() {
454 if (NULL != fBounder) {
455 fBounder->commit();
456 }
457 }
458private:
459 SkBounder* fBounder;
460};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000461#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462
463#include "SkColorPriv.h"
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465////////// macros to place around the internal draw calls //////////////////
466
reed@google.com8926b162012-03-23 15:36:36 +0000467#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000468 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000469 AutoDrawLooper looper(this, paint, true); \
470 while (looper.next(type)) { \
471 SkAutoBounderCommit ac(fBounder); \
472 SkDrawIter iter(this);
473
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000474#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000475 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000476 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000477 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkAutoBounderCommit ac(fBounder); \
479 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000480
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482
483////////////////////////////////////////////////////////////////////////////
484
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000485SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000487 fCachedLocalClipBounds.setEmpty();
488 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000489 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000490 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000491 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000492 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000493 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494
495 fMCRec = (MCRec*)fMCStack.push_back();
496 new (fMCRec) MCRec(NULL, 0);
497
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000498 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 fMCRec->fTopLayer = fMCRec->fLayer;
500 fMCRec->fNext = NULL;
501
reed@google.com97af1a62012-08-28 12:19:02 +0000502 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 return this->setDevice(device);
505}
506
reed@google.comcde92112011-07-06 20:00:52 +0000507SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000508 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
509{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000510 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000511
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000512 this->init(NULL);
513}
514
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000515SkCanvas::SkCanvas(int width, int height)
516 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
517{
518 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000519
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000520 SkBitmap bitmap;
521 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
522 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
523}
524
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000525SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
527{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 inc_canvas();
529
530 this->init(device);
531}
532
533SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
535{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 inc_canvas();
537
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000538 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539}
540
541SkCanvas::~SkCanvas() {
542 // free up the contents of our deque
543 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000544 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000545
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 this->internalRestore(); // restore the last, since we're going away
547
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000548 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000549 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000550
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 dec_canvas();
552}
553
554SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
555 SkRefCnt_SafeAssign(fBounder, bounder);
556 return bounder;
557}
558
559SkDrawFilter* SkCanvas::getDrawFilter() const {
560 return fMCRec->fFilter;
561}
562
563SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
564 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
565 return filter;
566}
567
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000568SkMetaData& SkCanvas::getMetaData() {
569 // metadata users are rare, so we lazily allocate it. If that changes we
570 // can decide to just make it a field in the device (rather than a ptr)
571 if (NULL == fMetaData) {
572 fMetaData = new SkMetaData;
573 }
574 return *fMetaData;
575}
576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577///////////////////////////////////////////////////////////////////////////////
578
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000579void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000580 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000581 if (device) {
582 device->flush();
583 }
584}
585
reed@google.com210ce002011-11-01 14:24:23 +0000586SkISize SkCanvas::getDeviceSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000587 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000588 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
589}
590
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000591SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000593 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkASSERT(rec && rec->fLayer);
595 return rec->fLayer->fDevice;
596}
597
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000598SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000599 if (updateMatrixClip) {
600 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
601 }
reed@google.com9266fed2011-03-30 00:18:03 +0000602 return fMCRec->fTopLayer->fDevice;
603}
604
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000605SkBaseDevice* SkCanvas::setDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000607 SkDeque::F2BIter iter(fMCStack);
608 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000610 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
612 if (rootDevice == device) {
613 return device;
614 }
reed@google.com4b226022011-01-11 18:32:13 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000617 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 }
619 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000620 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 }
622
623 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
624 rootDevice = device;
625
626 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000627
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 /* Now we update our initial region to have the bounds of the new device,
629 and then intersect all of the clips in our stack with these bounds,
630 to ensure that we can't draw outside of the device's bounds (and trash
631 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000632
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 NOTE: this is only a partial-fix, since if the new device is larger than
634 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000635 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
637 reconstruct the correct clips, so this approximation will have to do.
638 The caller really needs to restore() back to the base if they want to
639 accurately take advantage of the new device bounds.
640 */
641
reed@google.com42aea282012-03-28 16:19:15 +0000642 SkIRect bounds;
643 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000645 } else {
646 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 }
reed@google.com42aea282012-03-28 16:19:15 +0000648 // now jam our 1st clip to be bounds, and intersect the rest with that
649 rec->fRasterClip->setRect(bounds);
650 while ((rec = (MCRec*)iter.next()) != NULL) {
651 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
652 }
653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 return device;
655}
656
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000657bool SkCanvas::readPixels(SkBitmap* bitmap,
658 int x, int y,
659 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000660 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000661 if (!device) {
662 return false;
663 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000664 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000665}
666
bsalomon@google.comc6980972011-11-02 19:57:21 +0000667bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000668 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000669 if (!device) {
670 return false;
671 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000672
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000673 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000674 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000675 if (!bounds.intersect(srcRect)) {
676 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000677 }
678
679 SkBitmap tmp;
680 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
681 bounds.height());
682 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
683 bitmap->swap(tmp);
684 return true;
685 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000686 return false;
687 }
reed@google.com51df9e32010-12-23 19:29:18 +0000688}
689
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000690void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
691 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000692 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000693 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000694 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
695 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
696 device->accessBitmap(true);
697 device->writePixels(bitmap, x, y, config8888);
698 }
reed@google.com51df9e32010-12-23 19:29:18 +0000699 }
700}
701
junov@google.com4370aed2012-01-18 16:21:08 +0000702SkCanvas* SkCanvas::canvasForDrawIter() {
703 return this;
704}
705
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706//////////////////////////////////////////////////////////////////////////////
707
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708void SkCanvas::updateDeviceCMCache() {
709 if (fDeviceCMDirty) {
710 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000711 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000713
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000715 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000717 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000719 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 } while ((layer = layer->fNext) != NULL);
721 }
722 fDeviceCMDirty = false;
723 }
724}
725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726///////////////////////////////////////////////////////////////////////////////
727
728int SkCanvas::internalSave(SaveFlags flags) {
729 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000730
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 MCRec* newTop = (MCRec*)fMCStack.push_back();
732 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 newTop->fNext = fMCRec;
735 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000736
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000737 if (SkCanvas::kClip_SaveFlag & flags) {
738 fClipStack.save();
739 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000740
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 return saveCount;
742}
743
744int SkCanvas::save(SaveFlags flags) {
745 // call shared impl
746 return this->internalSave(flags);
747}
748
749#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
750#define C16MASK (1 << SkBitmap::kRGB_565_Config)
751#define C8MASK (1 << SkBitmap::kA8_Config)
752
753static SkBitmap::Config resolve_config(SkCanvas* canvas,
754 const SkIRect& bounds,
755 SkCanvas::SaveFlags flags,
756 bool* isOpaque) {
757 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
758
759#if 0
760 // loop through and union all the configs we may draw into
761 uint32_t configMask = 0;
762 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
763 {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000764 SkBaseDevice* device = canvas->getLayerDevice(i);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 if (device->intersects(bounds))
766 configMask |= 1 << device->config();
767 }
768
769 // if the caller wants alpha or fullcolor, we can't return 565
770 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
771 SkCanvas::kHasAlphaLayer_SaveFlag))
772 configMask &= ~C16MASK;
773
774 switch (configMask) {
775 case C8MASK: // if we only have A8, return that
776 return SkBitmap::kA8_Config;
777
778 case C16MASK: // if we only have 565, return that
779 return SkBitmap::kRGB_565_Config;
780
781 default:
782 return SkBitmap::kARGB_8888_Config; // default answer
783 }
784#else
785 return SkBitmap::kARGB_8888_Config; // default answer
786#endif
787}
788
789static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
790 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
791}
792
junov@chromium.orga907ac32012-02-24 21:54:07 +0000793bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000794 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000795 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000796 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000797 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000798 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000799 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000800
801 if (imageFilter) {
802 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
803 // Filters may grow the bounds beyond the device bounds.
804 op = SkRegion::kReplace_Op;
805 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000806 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 if (NULL != bounds) {
808 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 this->getTotalMatrix().mapRect(&r, *bounds);
811 r.roundOut(&ir);
812 // early exit if the layer's bounds are clipped out
813 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000814 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000815 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000816 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000817 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 }
819 } else { // no user bounds, so just use the clip
820 ir = clipBounds;
821 }
822
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000823 fClipStack.clipDevRect(ir, op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000824
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 // early exit if the clip is now empty
826 if (bounds_affects_clip(flags) &&
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000827 !fMCRec->fRasterClip->op(ir, op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000828 return false;
829 }
830
831 if (intersection) {
832 *intersection = ir;
833 }
834 return true;
835}
836
837int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
838 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000839 return this->internalSaveLayer(bounds, paint, flags, false);
840}
841
reed@google.com76f10a32014-02-05 15:32:21 +0000842static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
843 SkBitmap::Config config,
844 int width, int height,
845 bool isOpaque) {
846 SkBaseDevice* device = canvas->getDevice();
847 if (device) {
848 return device->createCompatibleDevice(config, width, height, isOpaque);
849 } else {
850 return NULL;
851 }
852}
853
reed@google.com8926b162012-03-23 15:36:36 +0000854int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
855 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856 // do this before we create the layer. We don't call the public save() since
857 // that would invoke a possibly overridden virtual
858 int count = this->internalSave(flags);
859
860 fDeviceCMDirty = true;
861
862 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000863 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 return count;
865 }
866
reed@google.comb55deeb2012-01-06 14:43:09 +0000867 // Kill the imagefilter if our device doesn't allow it
868 SkLazyPaint lazyP;
869 if (paint && paint->getImageFilter()) {
870 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000871 if (justForImageFilter) {
872 // early exit if the layer was just for the imageFilter
873 return count;
874 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000875 SkPaint* p = lazyP.set(*paint);
876 p->setImageFilter(NULL);
877 paint = p;
878 }
879 }
880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 bool isOpaque;
882 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
883
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000884 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000885 if (paint && paint->getImageFilter()) {
reed@google.com76f10a32014-02-05 15:32:21 +0000886 device = createCompatibleDevice(this, config, ir.width(), ir.height(),
887 isOpaque);
reed@google.com76dd2772012-01-05 21:15:07 +0000888 } else {
889 device = this->createLayerDevice(config, ir.width(), ir.height(),
890 isOpaque);
891 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000892 if (NULL == device) {
893 SkDebugf("Unable to create device for layer.");
894 return count;
895 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000896
reed@google.com6f8f2922011-03-04 22:27:10 +0000897 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000898 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 device->unref();
900
901 layer->fNext = fMCRec->fTopLayer;
902 fMCRec->fLayer = layer;
903 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
904
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000905 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 return count;
907}
908
909int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
910 SaveFlags flags) {
911 if (0xFF == alpha) {
912 return this->saveLayer(bounds, NULL, flags);
913 } else {
914 SkPaint tmpPaint;
915 tmpPaint.setAlpha(alpha);
916 return this->saveLayer(bounds, &tmpPaint, flags);
917 }
918}
919
920void SkCanvas::restore() {
921 // check for underflow
922 if (fMCStack.count() > 1) {
923 this->internalRestore();
924 }
925}
926
927void SkCanvas::internalRestore() {
928 SkASSERT(fMCStack.count() != 0);
929
930 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000931 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000933 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
934 fClipStack.restore();
935 }
936
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000937 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 DeviceCM* layer = fMCRec->fLayer; // may be null
939 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
940 fMCRec->fLayer = NULL;
941
942 // now do the normal restore()
943 fMCRec->~MCRec(); // balanced in save()
944 fMCStack.pop_back();
945 fMCRec = (MCRec*)fMCStack.back();
946
947 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
948 since if we're being recorded, we don't want to record this (the
949 recorder will have already recorded the restore).
950 */
951 if (NULL != layer) {
952 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000953 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000954 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
955 layer->fPaint);
956 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000958
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000959 SkASSERT(fSaveLayerCount > 0);
960 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 }
962 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000963 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964}
965
966int SkCanvas::getSaveCount() const {
967 return fMCStack.count();
968}
969
970void SkCanvas::restoreToCount(int count) {
971 // sanity check
972 if (count < 1) {
973 count = 1;
974 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000975
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000976 int n = this->getSaveCount() - count;
977 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 this->restore();
979 }
980}
981
reed@google.com7c202932011-12-14 18:48:05 +0000982bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000983 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000984}
985
reed@google.com76f10a32014-02-05 15:32:21 +0000986SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
987 return this->onNewSurface(info);
988}
989
990SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
991 SkBaseDevice* dev = this->getDevice();
992 return dev ? dev->newSurface(info) : NULL;
993}
994
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995/////////////////////////////////////////////////////////////////////////////
996
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000997void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000999 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 return;
1001 }
1002
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001003 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001005 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001007
1008 SkDEBUGCODE(bitmap.validate();)
1009 CHECK_LOCKCOUNT_BALANCE(bitmap);
1010
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001011 SkRect storage;
1012 const SkRect* bounds = NULL;
1013 if (paint && paint->canComputeFastBounds()) {
1014 bitmap.getBounds(&storage);
1015 matrix.mapRect(&storage);
1016 bounds = &paint->computeFastBounds(storage, &storage);
1017 }
1018
1019 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001020
1021 while (iter.next()) {
1022 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1023 }
1024
1025 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026}
1027
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001028void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001029 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 SkPaint tmp;
1031 if (NULL == paint) {
1032 tmp.setDither(true);
1033 paint = &tmp;
1034 }
reed@google.com4b226022011-01-11 18:32:13 +00001035
reed@google.com8926b162012-03-23 15:36:36 +00001036 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001038 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001039 paint = &looper.paint();
1040 SkImageFilter* filter = paint->getImageFilter();
1041 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001042 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001043 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001044 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001045 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001046 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001047 SkMatrix matrix = *iter.fMatrix;
1048 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001049 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001050 SkPaint tmpUnfiltered(*paint);
1051 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001052 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1053 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001054 }
1055 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001056 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001057 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001059 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060}
1061
reed@google.com8926b162012-03-23 15:36:36 +00001062void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1063 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001064 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001065 return;
1066 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001067 SkDEBUGCODE(bitmap.validate();)
1068 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001069
reed@google.com8926b162012-03-23 15:36:36 +00001070 SkPaint tmp;
1071 if (NULL == paint) {
1072 paint = &tmp;
1073 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001074
reed@google.com8926b162012-03-23 15:36:36 +00001075 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001076
reed@google.com8926b162012-03-23 15:36:36 +00001077 while (iter.next()) {
1078 paint = &looper.paint();
1079 SkImageFilter* filter = paint->getImageFilter();
1080 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1081 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001082 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001083 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001084 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001085 SkMatrix matrix = *iter.fMatrix;
1086 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001087 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001088 SkPaint tmpUnfiltered(*paint);
1089 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001090 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001091 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001092 }
1093 } else {
1094 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1095 }
1096 }
1097 LOOPER_END
1098}
1099
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100/////////////////////////////////////////////////////////////////////////////
1101
1102bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1103 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001104 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 return fMCRec->fMatrix->preTranslate(dx, dy);
1106}
1107
1108bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1109 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001110 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 return fMCRec->fMatrix->preScale(sx, sy);
1112}
1113
1114bool SkCanvas::rotate(SkScalar degrees) {
1115 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001116 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 return fMCRec->fMatrix->preRotate(degrees);
1118}
1119
1120bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1121 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001122 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 return fMCRec->fMatrix->preSkew(sx, sy);
1124}
1125
1126bool SkCanvas::concat(const SkMatrix& matrix) {
1127 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001128 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 return fMCRec->fMatrix->preConcat(matrix);
1130}
1131
1132void SkCanvas::setMatrix(const SkMatrix& matrix) {
1133 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001134 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 *fMCRec->fMatrix = matrix;
1136}
1137
1138// this is not virtual, so it must call a virtual method so that subclasses
1139// will see its action
1140void SkCanvas::resetMatrix() {
1141 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001142
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 matrix.reset();
1144 this->setMatrix(matrix);
1145}
1146
1147//////////////////////////////////////////////////////////////////////////////
1148
reed@google.comc42d35d2011-10-12 11:57:42 +00001149bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001150#ifdef SK_ENABLE_CLIP_QUICKREJECT
1151 if (SkRegion::kIntersect_Op == op) {
1152 if (fMCRec->fRasterClip->isEmpty()) {
1153 return false;
1154 }
1155
reed@google.com3b3e8952012-08-16 20:53:31 +00001156 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001157 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001158 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001159
1160 fClipStack.clipEmpty();
1161 return fMCRec->fRasterClip->setEmpty();
1162 }
1163 }
1164#endif
1165
reed@google.com5c3d1472011-02-22 19:12:23 +00001166 AutoValidateClip avc(this);
1167
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001169 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001170 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
1172 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001173 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001174 // the matrix. This means we don't have to a) make a path, and b) tell
1175 // the region code to scan-convert the path, only to discover that it
1176 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178
1179 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001180 fClipStack.clipDevRect(r, op, doAA);
1181 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001183 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001184 // and clip against that, since it can handle any matrix. However, to
1185 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1186 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 SkPath path;
1188
1189 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001190 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 }
1192}
1193
reed@google.com00177082011-10-12 14:34:30 +00001194static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001195 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001196 // base is used to limit the size (and therefore memory allocation) of the
1197 // region that results from scan converting devPath.
1198 SkRegion base;
1199
reed@google.com819c9212011-02-23 18:56:55 +00001200 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001201 // since we are intersect, we can do better (tighter) with currRgn's
1202 // bounds, than just using the device. However, if currRgn is complex,
1203 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001204 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001205 // FIXME: we should also be able to do this when currClip->isBW(),
1206 // but relaxing the test above triggers GM asserts in
1207 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1208 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001209 } else {
reed@google.com00177082011-10-12 14:34:30 +00001210 base.setRect(currClip->getBounds());
1211 SkRasterClip clip;
1212 clip.setPath(devPath, base, doAA);
1213 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001214 }
reed@google.com819c9212011-02-23 18:56:55 +00001215 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001216 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001217 if (!device) {
1218 return currClip->setEmpty();
1219 }
1220
junov@chromium.orga907ac32012-02-24 21:54:07 +00001221 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001222
1223 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001224 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001225 } else {
reed@google.com00177082011-10-12 14:34:30 +00001226 SkRasterClip clip;
1227 clip.setPath(devPath, base, doAA);
1228 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001229 }
1230 }
1231}
1232
reed@google.com4ed0fb72012-12-12 20:48:18 +00001233bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1234 if (rrect.isRect()) {
1235 // call the non-virtual version
1236 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1237 } else {
1238 SkPath path;
1239 path.addRRect(rrect);
1240 // call the non-virtual version
1241 return this->SkCanvas::clipPath(path, op, doAA);
1242 }
1243}
1244
reed@google.comc42d35d2011-10-12 11:57:42 +00001245bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001246#ifdef SK_ENABLE_CLIP_QUICKREJECT
1247 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1248 if (fMCRec->fRasterClip->isEmpty()) {
1249 return false;
1250 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001251
reed@google.com3b3e8952012-08-16 20:53:31 +00001252 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001253 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001254 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001255
reed@google.comda17f752012-08-16 18:27:05 +00001256 fClipStack.clipEmpty();
1257 return fMCRec->fRasterClip->setEmpty();
1258 }
1259 }
1260#endif
1261
reed@google.com5c3d1472011-02-22 19:12:23 +00001262 AutoValidateClip avc(this);
1263
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001265 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001266 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267
1268 SkPath devPath;
1269 path.transform(*fMCRec->fMatrix, &devPath);
1270
reed@google.comfe701122011-11-08 19:41:23 +00001271 // Check if the transfomation, or the original path itself
1272 // made us empty. Note this can also happen if we contained NaN
1273 // values. computing the bounds detects this, and will set our
1274 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1275 if (devPath.getBounds().isEmpty()) {
1276 // resetting the path will remove any NaN or other wanky values
1277 // that might upset our scan converter.
1278 devPath.reset();
1279 }
1280
reed@google.com5c3d1472011-02-22 19:12:23 +00001281 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001282 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001283
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001284 if (fAllowSimplifyClip) {
1285 devPath.reset();
1286 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1287 const SkClipStack* clipStack = getClipStack();
1288 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1289 const SkClipStack::Element* element;
1290 while ((element = iter.next())) {
1291 SkClipStack::Element::Type type = element->getType();
1292 if (type == SkClipStack::Element::kEmpty_Type) {
1293 continue;
1294 }
1295 SkPath operand;
1296 if (type == SkClipStack::Element::kRect_Type) {
1297 operand.addRect(element->getRect());
1298 } else if (type == SkClipStack::Element::kPath_Type) {
1299 operand = element->getPath();
1300 } else {
1301 SkDEBUGFAIL("Unexpected type.");
1302 }
1303 SkRegion::Op elementOp = element->getOp();
1304 if (elementOp == SkRegion::kReplace_Op) {
1305 devPath = operand;
1306 } else {
1307 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1308 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001309 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1310 // perhaps we need an API change to avoid this sort of mixed-signals about
1311 // clipping.
1312 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001313 }
1314 op = SkRegion::kReplace_Op;
1315 }
1316
reed@google.com00177082011-10-12 14:34:30 +00001317 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318}
1319
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001320bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1321 bool inverseFilled) {
1322 // This is for updating the clip conservatively using only bounds
1323 // information.
1324 // Contract:
1325 // The current clip must contain the true clip. The true
1326 // clip is the clip that would have normally been computed
1327 // by calls to clipPath and clipRRect
1328 // Objective:
1329 // Keep the current clip as small as possible without
1330 // breaking the contract, using only clip bounding rectangles
1331 // (for performance).
1332
1333 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1334 // don't have to worry about getting caught in a loop. Thus anywhere
1335 // we call a virtual method, we explicitly prefix it with
1336 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001337
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001338 if (inverseFilled) {
1339 switch (op) {
1340 case SkRegion::kIntersect_Op:
1341 case SkRegion::kDifference_Op:
1342 // These ops can only shrink the current clip. So leaving
1343 // the clip unchanges conservatively respects the contract.
1344 return this->getClipDeviceBounds(NULL);
1345 case SkRegion::kUnion_Op:
1346 case SkRegion::kReplace_Op:
1347 case SkRegion::kReverseDifference_Op:
1348 case SkRegion::kXOR_Op:
1349 {
1350 // These ops can grow the current clip up to the extents of
1351 // the input clip, which is inverse filled, so we just set
1352 // the current clip to the device bounds.
1353 SkRect deviceBounds;
1354 SkIRect deviceIBounds;
1355 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001356 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001357 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1358 // set the clip in device space
1359 this->SkCanvas::setMatrix(SkMatrix::I());
1360 bool result = this->SkCanvas::clipRect(deviceBounds,
1361 SkRegion::kReplace_Op, false);
1362 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1363 return result;
1364 }
1365 default:
1366 SkASSERT(0); // unhandled op?
1367 }
1368 } else {
1369 // Not inverse filled
1370 switch (op) {
1371 case SkRegion::kIntersect_Op:
1372 case SkRegion::kUnion_Op:
1373 case SkRegion::kReplace_Op:
1374 return this->SkCanvas::clipRect(bounds, op, false);
1375 case SkRegion::kDifference_Op:
1376 // Difference can only shrink the current clip.
1377 // Leaving clip unchanged conservatively fullfills the contract.
1378 return this->getClipDeviceBounds(NULL);
1379 case SkRegion::kReverseDifference_Op:
1380 // To reverse, we swap in the bounds with a replace op.
1381 // As with difference, leave it unchanged.
1382 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1383 case SkRegion::kXOR_Op:
1384 // Be conservative, based on (A XOR B) always included in (A union B),
1385 // which is always included in (bounds(A) union bounds(B))
1386 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1387 default:
1388 SkASSERT(0); // unhandled op?
1389 }
1390 }
1391 return true;
1392}
1393
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001395 AutoValidateClip avc(this);
1396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001398 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399
reed@google.com5c3d1472011-02-22 19:12:23 +00001400 // todo: signal fClipStack that we have a region, and therefore (I guess)
1401 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001402 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001403
reed@google.com00177082011-10-12 14:34:30 +00001404 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405}
1406
reed@google.com819c9212011-02-23 18:56:55 +00001407#ifdef SK_DEBUG
1408void SkCanvas::validateClip() const {
1409 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001410 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001411 if (!device) {
1412 SkASSERT(this->getTotalClip().isEmpty());
1413 return;
1414 }
1415
reed@google.com819c9212011-02-23 18:56:55 +00001416 SkIRect ir;
1417 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001418 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001419
robertphillips@google.com80214e22012-07-20 15:33:18 +00001420 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001421 const SkClipStack::Element* element;
1422 while ((element = iter.next()) != NULL) {
1423 switch (element->getType()) {
1424 case SkClipStack::Element::kPath_Type:
1425 clipPathHelper(this,
1426 &tmpClip,
1427 element->getPath(),
1428 element->getOp(),
1429 element->isAA());
1430 break;
1431 case SkClipStack::Element::kRect_Type:
1432 element->getRect().round(&ir);
1433 tmpClip.op(ir, element->getOp());
1434 break;
1435 case SkClipStack::Element::kEmpty_Type:
1436 tmpClip.setEmpty();
1437 break;
reed@google.com819c9212011-02-23 18:56:55 +00001438 }
1439 }
1440
reed@google.com6f8f2922011-03-04 22:27:10 +00001441#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001442 // now compare against the current rgn
1443 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001444 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001445#endif
reed@google.com819c9212011-02-23 18:56:55 +00001446}
1447#endif
1448
reed@google.com90c07ea2012-04-13 13:50:27 +00001449void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001450 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001451 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001452
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001453 static const SkRect kEmpty = { 0, 0, 0, 0 };
1454 while ((element = iter.next()) != NULL) {
1455 switch (element->getType()) {
1456 case SkClipStack::Element::kPath_Type:
1457 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1458 break;
1459 case SkClipStack::Element::kRect_Type:
1460 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1461 break;
1462 case SkClipStack::Element::kEmpty_Type:
1463 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1464 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001465 }
1466 }
1467}
1468
reed@google.com5c3d1472011-02-22 19:12:23 +00001469///////////////////////////////////////////////////////////////////////////////
1470
reed@google.com3b3e8952012-08-16 20:53:31 +00001471bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001472
reed@google.com16078632011-12-06 18:56:37 +00001473 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001474 return true;
1475
reed@google.com00177082011-10-12 14:34:30 +00001476 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 return true;
1478 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479
tomhudson@google.com8d430182011-06-06 19:11:19 +00001480 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001481 SkRect dst;
1482 fMCRec->fMatrix->mapRect(&dst, rect);
1483 SkIRect idst;
1484 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001485 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001486 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001487 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001488
reed@android.coma380ae42009-07-21 01:17:02 +00001489 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001490 // TODO: should we use | instead, or compare all 4 at once?
1491 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001492 return true;
1493 }
reed@google.comc0784db2013-12-13 21:16:12 +00001494 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001495 return true;
1496 }
1497 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499}
1500
reed@google.com3b3e8952012-08-16 20:53:31 +00001501bool SkCanvas::quickReject(const SkPath& path) const {
1502 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503}
1504
reed@google.com3b3e8952012-08-16 20:53:31 +00001505bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001506 SkIRect ibounds;
1507 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 return false;
1509 }
1510
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001511 SkMatrix inverse;
1512 // if we can't invert the CTM, we can't return local clip bounds
1513 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001514 if (bounds) {
1515 bounds->setEmpty();
1516 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001517 return false;
1518 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001520 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001521 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001522 // adjust it outwards in case we are antialiasing
1523 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001524
reed@google.com8f4d2302013-12-17 16:44:46 +00001525 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1526 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527 inverse.mapRect(bounds, r);
1528 }
1529 return true;
1530}
1531
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001532bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001533 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001534 if (clip.isEmpty()) {
1535 if (bounds) {
1536 bounds->setEmpty();
1537 }
1538 return false;
1539 }
1540
1541 if (NULL != bounds) {
1542 *bounds = clip.getBounds();
1543 }
1544 return true;
1545}
1546
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547const SkMatrix& SkCanvas::getTotalMatrix() const {
1548 return *fMCRec->fMatrix;
1549}
1550
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001551SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001552 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1553 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001554 return kComplex_ClipType;
1555}
1556
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001558 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559}
1560
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001561SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001562 int width, int height,
1563 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001564 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001565 if (device) {
1566 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1567 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001568 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001569 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001570 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571}
1572
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001573GrContext* SkCanvas::getGrContext() {
1574#if SK_SUPPORT_GPU
1575 SkBaseDevice* device = this->getTopDevice();
1576 if (NULL != device) {
1577 GrRenderTarget* renderTarget = device->accessRenderTarget();
1578 if (NULL != renderTarget) {
1579 return renderTarget->getContext();
1580 }
1581 }
1582#endif
1583
1584 return NULL;
1585
1586}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001587
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588//////////////////////////////////////////////////////////////////////////////
1589// These are the virtual drawing methods
1590//////////////////////////////////////////////////////////////////////////////
1591
reed@google.com2a981812011-04-14 18:59:28 +00001592void SkCanvas::clear(SkColor color) {
1593 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001594 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001595 while (iter.next()) {
1596 iter.fDevice->clear(color);
1597 }
1598}
1599
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001601 this->internalDrawPaint(paint);
1602}
1603
1604void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001605 CHECK_SHADER_NOSETCONTEXT(paint);
1606
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001607 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608
1609 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001610 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 }
1612
reed@google.com4e2b3d32011-04-07 14:18:59 +00001613 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614}
1615
1616void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1617 const SkPaint& paint) {
1618 if ((long)count <= 0) {
1619 return;
1620 }
1621
reed@google.comea033602012-12-14 13:13:55 +00001622 CHECK_SHADER_NOSETCONTEXT(paint);
1623
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001624 SkRect r, storage;
1625 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001626 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001627 // special-case 2 points (common for drawing a single line)
1628 if (2 == count) {
1629 r.set(pts[0], pts[1]);
1630 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001631 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001632 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001633 bounds = &paint.computeFastStrokeBounds(r, &storage);
1634 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001635 return;
1636 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001637 }
reed@google.coma584aed2012-05-16 14:06:02 +00001638
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639 SkASSERT(pts != NULL);
1640
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001641 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001642
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001644 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645 }
reed@google.com4b226022011-01-11 18:32:13 +00001646
reed@google.com4e2b3d32011-04-07 14:18:59 +00001647 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648}
1649
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001650void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001651 CHECK_SHADER_NOSETCONTEXT(paint);
1652
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001653 SkRect storage;
1654 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001656 bounds = &paint.computeFastBounds(r, &storage);
1657 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658 return;
1659 }
1660 }
reed@google.com4b226022011-01-11 18:32:13 +00001661
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001662 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663
1664 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001665 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001666 }
1667
reed@google.com4e2b3d32011-04-07 14:18:59 +00001668 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669}
1670
reed@google.com4ed0fb72012-12-12 20:48:18 +00001671void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001672 CHECK_SHADER_NOSETCONTEXT(paint);
1673
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001674 SkRect storage;
1675 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001676 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001677 bounds = &paint.computeFastBounds(oval, &storage);
1678 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001679 return;
1680 }
1681 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001682
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001683 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001684
1685 while (iter.next()) {
1686 iter.fDevice->drawOval(iter, oval, looper.paint());
1687 }
1688
1689 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001690}
1691
1692void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001693 CHECK_SHADER_NOSETCONTEXT(paint);
1694
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001695 SkRect storage;
1696 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001697 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001698 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1699 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001700 return;
1701 }
1702 }
1703
1704 if (rrect.isRect()) {
1705 // call the non-virtual version
1706 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001707 return;
1708 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001709 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001710 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1711 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001712 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001713
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001714 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001715
1716 while (iter.next()) {
1717 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1718 }
1719
1720 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001721}
1722
1723
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001724void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001725 CHECK_SHADER_NOSETCONTEXT(paint);
1726
reed@google.com93645112012-07-26 16:11:47 +00001727 if (!path.isFinite()) {
1728 return;
1729 }
1730
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001731 SkRect storage;
1732 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001733 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001734 const SkRect& pathBounds = path.getBounds();
1735 bounds = &paint.computeFastBounds(pathBounds, &storage);
1736 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 return;
1738 }
1739 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001740 if (path.isEmpty()) {
1741 if (path.isInverseFillType()) {
1742 this->internalDrawPaint(paint);
1743 }
1744 return;
1745 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001747 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748
1749 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001750 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 }
1752
reed@google.com4e2b3d32011-04-07 14:18:59 +00001753 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754}
1755
1756void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1757 const SkPaint* paint) {
1758 SkDEBUGCODE(bitmap.validate();)
1759
reed@google.com3d608122011-11-21 15:16:16 +00001760 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001761 SkRect bounds = {
1762 x, y,
1763 x + SkIntToScalar(bitmap.width()),
1764 y + SkIntToScalar(bitmap.height())
1765 };
1766 if (paint) {
1767 (void)paint->computeFastBounds(bounds, &bounds);
1768 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001769 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770 return;
1771 }
1772 }
reed@google.com4b226022011-01-11 18:32:13 +00001773
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 SkMatrix matrix;
1775 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001776 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777}
1778
reed@google.com9987ec32011-09-07 11:57:52 +00001779// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001780void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001781 const SkRect& dst, const SkPaint* paint,
1782 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001783 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784 return;
1785 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001786
reed@google.comea033602012-12-14 13:13:55 +00001787 CHECK_LOCKCOUNT_BALANCE(bitmap);
1788
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001789 SkRect storage;
1790 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001791 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001792 if (paint) {
1793 bounds = &paint->computeFastBounds(dst, &storage);
1794 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001795 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001796 return;
1797 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 }
reed@google.com3d608122011-11-21 15:16:16 +00001799
reed@google.com33535f32012-09-25 15:37:50 +00001800 SkLazyPaint lazy;
1801 if (NULL == paint) {
1802 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001804
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001805 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001806
reed@google.com33535f32012-09-25 15:37:50 +00001807 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001808 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001809 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001810
reed@google.com33535f32012-09-25 15:37:50 +00001811 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812}
1813
reed@google.com71121732012-09-18 15:14:33 +00001814void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001815 const SkRect& dst, const SkPaint* paint,
1816 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001817 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001818 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001819}
1820
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1822 const SkPaint* paint) {
1823 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001824 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825}
1826
reed@google.com9987ec32011-09-07 11:57:52 +00001827void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1828 const SkIRect& center, const SkRect& dst,
1829 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001830 if (bitmap.drawsNothing()) {
1831 return;
1832 }
reed@google.com3d608122011-11-21 15:16:16 +00001833 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001834 SkRect storage;
1835 const SkRect* bounds = &dst;
1836 if (paint) {
1837 bounds = &paint->computeFastBounds(dst, &storage);
1838 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001839 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001840 return;
1841 }
1842 }
1843
reed@google.com9987ec32011-09-07 11:57:52 +00001844 const int32_t w = bitmap.width();
1845 const int32_t h = bitmap.height();
1846
1847 SkIRect c = center;
1848 // pin center to the bounds of the bitmap
1849 c.fLeft = SkMax32(0, center.fLeft);
1850 c.fTop = SkMax32(0, center.fTop);
1851 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1852 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1853
reed@google.com71121732012-09-18 15:14:33 +00001854 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001855 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001856 };
1857 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001858 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001859 };
reed@google.com9987ec32011-09-07 11:57:52 +00001860 SkScalar dstX[4] = {
1861 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1862 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1863 };
1864 SkScalar dstY[4] = {
1865 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1866 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1867 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001868
reed@google.com9987ec32011-09-07 11:57:52 +00001869 if (dstX[1] > dstX[2]) {
1870 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1871 dstX[2] = dstX[1];
1872 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001873
reed@google.com9987ec32011-09-07 11:57:52 +00001874 if (dstY[1] > dstY[2]) {
1875 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1876 dstY[2] = dstY[1];
1877 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001878
reed@google.com9987ec32011-09-07 11:57:52 +00001879 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001880 SkRect s, d;
1881
reed@google.com9987ec32011-09-07 11:57:52 +00001882 s.fTop = srcY[y];
1883 s.fBottom = srcY[y+1];
1884 d.fTop = dstY[y];
1885 d.fBottom = dstY[y+1];
1886 for (int x = 0; x < 3; x++) {
1887 s.fLeft = srcX[x];
1888 s.fRight = srcX[x+1];
1889 d.fLeft = dstX[x];
1890 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001891 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001892 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001893 }
1894 }
1895}
1896
1897void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1898 const SkRect& dst, const SkPaint* paint) {
1899 SkDEBUGCODE(bitmap.validate();)
1900
1901 // Need a device entry-point, so gpu can use a mesh
1902 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1903}
1904
reed@google.comf67e4cf2011-03-15 20:56:58 +00001905class SkDeviceFilteredPaint {
1906public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001907 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1908 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001909 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001910 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001911 newPaint->setFlags(flags.fFlags);
1912 newPaint->setHinting(flags.fHinting);
1913 fPaint = newPaint;
1914 } else {
1915 fPaint = &paint;
1916 }
1917 }
1918
reed@google.comf67e4cf2011-03-15 20:56:58 +00001919 const SkPaint& paint() const { return *fPaint; }
1920
1921private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001922 const SkPaint* fPaint;
1923 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001924};
1925
bungeman@google.com52c748b2011-08-22 21:30:43 +00001926void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1927 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001928 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001929 draw.fDevice->drawRect(draw, r, paint);
1930 } else {
1931 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001932 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001933 draw.fDevice->drawRect(draw, r, p);
1934 }
1935}
1936
1937void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1938 const char text[], size_t byteLength,
1939 SkScalar x, SkScalar y) {
1940 SkASSERT(byteLength == 0 || text != NULL);
1941
1942 // nothing to draw
1943 if (text == NULL || byteLength == 0 ||
1944 draw.fClip->isEmpty() ||
1945 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1946 return;
1947 }
1948
1949 SkScalar width = 0;
1950 SkPoint start;
1951
1952 start.set(0, 0); // to avoid warning
1953 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1954 SkPaint::kStrikeThruText_Flag)) {
1955 width = paint.measureText(text, byteLength);
1956
1957 SkScalar offsetX = 0;
1958 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1959 offsetX = SkScalarHalf(width);
1960 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1961 offsetX = width;
1962 }
1963 start.set(x - offsetX, y);
1964 }
1965
1966 if (0 == width) {
1967 return;
1968 }
1969
1970 uint32_t flags = paint.getFlags();
1971
1972 if (flags & (SkPaint::kUnderlineText_Flag |
1973 SkPaint::kStrikeThruText_Flag)) {
1974 SkScalar textSize = paint.getTextSize();
1975 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1976 SkRect r;
1977
1978 r.fLeft = start.fX;
1979 r.fRight = start.fX + width;
1980
1981 if (flags & SkPaint::kUnderlineText_Flag) {
1982 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1983 start.fY);
1984 r.fTop = offset;
1985 r.fBottom = offset + height;
1986 DrawRect(draw, paint, r, textSize);
1987 }
1988 if (flags & SkPaint::kStrikeThruText_Flag) {
1989 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1990 start.fY);
1991 r.fTop = offset;
1992 r.fBottom = offset + height;
1993 DrawRect(draw, paint, r, textSize);
1994 }
1995 }
1996}
1997
reed@android.com8a1c16f2008-12-17 15:59:43 +00001998void SkCanvas::drawText(const void* text, size_t byteLength,
1999 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002000 CHECK_SHADER_NOSETCONTEXT(paint);
2001
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002002 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003
2004 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002005 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002006 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002007 DrawTextDecorations(iter, dfp.paint(),
2008 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009 }
2010
reed@google.com4e2b3d32011-04-07 14:18:59 +00002011 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012}
2013
2014void SkCanvas::drawPosText(const void* text, size_t byteLength,
2015 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002016 CHECK_SHADER_NOSETCONTEXT(paint);
2017
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002018 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002019
reed@android.com8a1c16f2008-12-17 15:59:43 +00002020 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002021 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002023 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024 }
reed@google.com4b226022011-01-11 18:32:13 +00002025
reed@google.com4e2b3d32011-04-07 14:18:59 +00002026 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002027}
2028
2029void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2030 const SkScalar xpos[], SkScalar constY,
2031 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002032 CHECK_SHADER_NOSETCONTEXT(paint);
2033
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002034 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002035
reed@android.com8a1c16f2008-12-17 15:59:43 +00002036 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002037 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002039 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 }
reed@google.com4b226022011-01-11 18:32:13 +00002041
reed@google.com4e2b3d32011-04-07 14:18:59 +00002042 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043}
2044
2045void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2046 const SkPath& path, const SkMatrix* matrix,
2047 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002048 CHECK_SHADER_NOSETCONTEXT(paint);
2049
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002050 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051
2052 while (iter.next()) {
2053 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002054 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055 }
2056
reed@google.com4e2b3d32011-04-07 14:18:59 +00002057 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058}
2059
2060void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2061 const SkPoint verts[], const SkPoint texs[],
2062 const SkColor colors[], SkXfermode* xmode,
2063 const uint16_t indices[], int indexCount,
2064 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002065 CHECK_SHADER_NOSETCONTEXT(paint);
2066
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002067 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002068
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069 while (iter.next()) {
2070 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002071 colors, xmode, indices, indexCount,
2072 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 }
reed@google.com4b226022011-01-11 18:32:13 +00002074
reed@google.com4e2b3d32011-04-07 14:18:59 +00002075 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076}
2077
2078//////////////////////////////////////////////////////////////////////////////
2079// These methods are NOT virtual, and therefore must call back into virtual
2080// methods, rather than actually drawing themselves.
2081//////////////////////////////////////////////////////////////////////////////
2082
2083void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002084 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 SkPaint paint;
2086
2087 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002088 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002089 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 }
2091 this->drawPaint(paint);
2092}
2093
reed@android.com845fdac2009-06-23 03:01:32 +00002094void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 SkPaint paint;
2096
2097 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002098 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002099 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 }
2101 this->drawPaint(paint);
2102}
2103
2104void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2105 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002106
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107 pt.set(x, y);
2108 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2109}
2110
2111void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2112 SkPoint pt;
2113 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002114
reed@android.com8a1c16f2008-12-17 15:59:43 +00002115 pt.set(x, y);
2116 paint.setColor(color);
2117 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2118}
2119
2120void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2121 const SkPaint& paint) {
2122 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002123
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 pts[0].set(x0, y0);
2125 pts[1].set(x1, y1);
2126 this->drawPoints(kLines_PointMode, 2, pts, paint);
2127}
2128
2129void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2130 SkScalar right, SkScalar bottom,
2131 const SkPaint& paint) {
2132 SkRect r;
2133
2134 r.set(left, top, right, bottom);
2135 this->drawRect(r, paint);
2136}
2137
2138void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2139 const SkPaint& paint) {
2140 if (radius < 0) {
2141 radius = 0;
2142 }
2143
2144 SkRect r;
2145 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002146 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147}
2148
2149void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2150 const SkPaint& paint) {
2151 if (rx > 0 && ry > 0) {
2152 if (paint.canComputeFastBounds()) {
2153 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002154 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002155 return;
2156 }
2157 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002158 SkRRect rrect;
2159 rrect.setRectXY(r, rx, ry);
2160 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161 } else {
2162 this->drawRect(r, paint);
2163 }
2164}
2165
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2167 SkScalar sweepAngle, bool useCenter,
2168 const SkPaint& paint) {
2169 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2170 this->drawOval(oval, paint);
2171 } else {
2172 SkPath path;
2173 if (useCenter) {
2174 path.moveTo(oval.centerX(), oval.centerY());
2175 }
2176 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2177 if (useCenter) {
2178 path.close();
2179 }
2180 this->drawPath(path, paint);
2181 }
2182}
2183
2184void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2185 const SkPath& path, SkScalar hOffset,
2186 SkScalar vOffset, const SkPaint& paint) {
2187 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002188
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 matrix.setTranslate(hOffset, vOffset);
2190 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2191}
2192
reed@android.comf76bacf2009-05-13 14:00:33 +00002193///////////////////////////////////////////////////////////////////////////////
2194
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197}
2198
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199///////////////////////////////////////////////////////////////////////////////
2200///////////////////////////////////////////////////////////////////////////////
2201
2202SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002203 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204
2205 SkASSERT(canvas);
2206
2207 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2208 fDone = !fImpl->next();
2209}
2210
2211SkCanvas::LayerIter::~LayerIter() {
2212 fImpl->~SkDrawIter();
2213}
2214
2215void SkCanvas::LayerIter::next() {
2216 fDone = !fImpl->next();
2217}
2218
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002219SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220 return fImpl->getDevice();
2221}
2222
2223const SkMatrix& SkCanvas::LayerIter::matrix() const {
2224 return fImpl->getMatrix();
2225}
2226
2227const SkPaint& SkCanvas::LayerIter::paint() const {
2228 const SkPaint* paint = fImpl->getPaint();
2229 if (NULL == paint) {
2230 paint = &fDefaultPaint;
2231 }
2232 return *paint;
2233}
2234
2235const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2236int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2237int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002238
2239///////////////////////////////////////////////////////////////////////////////
2240
2241SkCanvas::ClipVisitor::~ClipVisitor() { }