blob: 6f4e88da863496e2d161a01a98948731612b36cc [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,
794 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000795 SkIRect clipBounds;
796 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000797 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000798 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000799 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 if (NULL != bounds) {
801 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000802
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 this->getTotalMatrix().mapRect(&r, *bounds);
804 r.roundOut(&ir);
805 // early exit if the layer's bounds are clipped out
806 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000807 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000808 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000809 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 }
812 } else { // no user bounds, so just use the clip
813 ir = clipBounds;
814 }
815
reed@google.com5c3d1472011-02-22 19:12:23 +0000816 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000817
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 // early exit if the clip is now empty
819 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000820 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000821 return false;
822 }
823
824 if (intersection) {
825 *intersection = ir;
826 }
827 return true;
828}
829
830int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
831 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000832 return this->internalSaveLayer(bounds, paint, flags, false);
833}
834
reed@google.com76f10a32014-02-05 15:32:21 +0000835static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
836 SkBitmap::Config config,
837 int width, int height,
838 bool isOpaque) {
839 SkBaseDevice* device = canvas->getDevice();
840 if (device) {
841 return device->createCompatibleDevice(config, width, height, isOpaque);
842 } else {
843 return NULL;
844 }
845}
846
847#ifdef SK_SUPPORT_LEGACY_CANVAS_CREATECOMPATIBLEDEVICE
848SkBaseDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
849 int width, int height,
850 bool isOpaque) {
851 return createCompatibleDevice(this, config, width, height, isOpaque);
852}
853#endif
854
reed@google.com8926b162012-03-23 15:36:36 +0000855int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
856 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000857 // do this before we create the layer. We don't call the public save() since
858 // that would invoke a possibly overridden virtual
859 int count = this->internalSave(flags);
860
861 fDeviceCMDirty = true;
862
863 SkIRect ir;
864 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 return count;
866 }
867
reed@google.comb55deeb2012-01-06 14:43:09 +0000868 // Kill the imagefilter if our device doesn't allow it
869 SkLazyPaint lazyP;
870 if (paint && paint->getImageFilter()) {
871 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000872 if (justForImageFilter) {
873 // early exit if the layer was just for the imageFilter
874 return count;
875 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000876 SkPaint* p = lazyP.set(*paint);
877 p->setImageFilter(NULL);
878 paint = p;
879 }
880 }
881
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 bool isOpaque;
883 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
884
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000885 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000886 if (paint && paint->getImageFilter()) {
reed@google.com76f10a32014-02-05 15:32:21 +0000887 device = createCompatibleDevice(this, config, ir.width(), ir.height(),
888 isOpaque);
reed@google.com76dd2772012-01-05 21:15:07 +0000889 } else {
890 device = this->createLayerDevice(config, ir.width(), ir.height(),
891 isOpaque);
892 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000893 if (NULL == device) {
894 SkDebugf("Unable to create device for layer.");
895 return count;
896 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000897
reed@google.com6f8f2922011-03-04 22:27:10 +0000898 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000899 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 device->unref();
901
902 layer->fNext = fMCRec->fTopLayer;
903 fMCRec->fLayer = layer;
904 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
905
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000906 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 return count;
908}
909
910int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
911 SaveFlags flags) {
912 if (0xFF == alpha) {
913 return this->saveLayer(bounds, NULL, flags);
914 } else {
915 SkPaint tmpPaint;
916 tmpPaint.setAlpha(alpha);
917 return this->saveLayer(bounds, &tmpPaint, flags);
918 }
919}
920
921void SkCanvas::restore() {
922 // check for underflow
923 if (fMCStack.count() > 1) {
924 this->internalRestore();
925 }
926}
927
928void SkCanvas::internalRestore() {
929 SkASSERT(fMCStack.count() != 0);
930
931 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000932 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000934 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
935 fClipStack.restore();
936 }
937
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000938 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 DeviceCM* layer = fMCRec->fLayer; // may be null
940 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
941 fMCRec->fLayer = NULL;
942
943 // now do the normal restore()
944 fMCRec->~MCRec(); // balanced in save()
945 fMCStack.pop_back();
946 fMCRec = (MCRec*)fMCStack.back();
947
948 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
949 since if we're being recorded, we don't want to record this (the
950 recorder will have already recorded the restore).
951 */
952 if (NULL != layer) {
953 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000954 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000955 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
956 layer->fPaint);
957 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000959
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000960 SkASSERT(fSaveLayerCount > 0);
961 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 }
963 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000964 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965}
966
967int SkCanvas::getSaveCount() const {
968 return fMCStack.count();
969}
970
971void SkCanvas::restoreToCount(int count) {
972 // sanity check
973 if (count < 1) {
974 count = 1;
975 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000976
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000977 int n = this->getSaveCount() - count;
978 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 this->restore();
980 }
981}
982
reed@google.com7c202932011-12-14 18:48:05 +0000983bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000984 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000985}
986
reed@google.com76f10a32014-02-05 15:32:21 +0000987SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
988 return this->onNewSurface(info);
989}
990
991SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
992 SkBaseDevice* dev = this->getDevice();
993 return dev ? dev->newSurface(info) : NULL;
994}
995
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996/////////////////////////////////////////////////////////////////////////////
997
998// can't draw it if its empty, or its too big for a fixed-point width or height
999static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +00001000 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001}
1002
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001003void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 const SkMatrix& matrix, const SkPaint* paint) {
1005 if (reject_bitmap(bitmap)) {
1006 return;
1007 }
1008
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001009 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001011 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001013
1014 SkDEBUGCODE(bitmap.validate();)
1015 CHECK_LOCKCOUNT_BALANCE(bitmap);
1016
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001017 SkRect storage;
1018 const SkRect* bounds = NULL;
1019 if (paint && paint->canComputeFastBounds()) {
1020 bitmap.getBounds(&storage);
1021 matrix.mapRect(&storage);
1022 bounds = &paint->computeFastBounds(storage, &storage);
1023 }
1024
1025 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001026
1027 while (iter.next()) {
1028 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1029 }
1030
1031 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032}
1033
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001034void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001035 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 SkPaint tmp;
1037 if (NULL == paint) {
1038 tmp.setDither(true);
1039 paint = &tmp;
1040 }
reed@google.com4b226022011-01-11 18:32:13 +00001041
reed@google.com8926b162012-03-23 15:36:36 +00001042 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001044 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001045 paint = &looper.paint();
1046 SkImageFilter* filter = paint->getImageFilter();
1047 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001048 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001049 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001050 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001051 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001052 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001053 SkMatrix matrix = *iter.fMatrix;
1054 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001055 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001056 SkPaint tmpUnfiltered(*paint);
1057 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001058 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1059 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001060 }
1061 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001062 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001063 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001065 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066}
1067
reed@google.com8926b162012-03-23 15:36:36 +00001068void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1069 const SkPaint* paint) {
1070 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001071 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001072
reed@google.com8926b162012-03-23 15:36:36 +00001073 if (reject_bitmap(bitmap)) {
1074 return;
1075 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001076
reed@google.com8926b162012-03-23 15:36:36 +00001077 SkPaint tmp;
1078 if (NULL == paint) {
1079 paint = &tmp;
1080 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001081
reed@google.com8926b162012-03-23 15:36:36 +00001082 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001083
reed@google.com8926b162012-03-23 15:36:36 +00001084 while (iter.next()) {
1085 paint = &looper.paint();
1086 SkImageFilter* filter = paint->getImageFilter();
1087 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1088 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001089 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001090 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001091 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001092 SkMatrix matrix = *iter.fMatrix;
1093 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001094 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001095 SkPaint tmpUnfiltered(*paint);
1096 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001097 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001098 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001099 }
1100 } else {
1101 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1102 }
1103 }
1104 LOOPER_END
1105}
1106
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107/////////////////////////////////////////////////////////////////////////////
1108
1109bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1110 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001111 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 return fMCRec->fMatrix->preTranslate(dx, dy);
1113}
1114
1115bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1116 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001117 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 return fMCRec->fMatrix->preScale(sx, sy);
1119}
1120
1121bool SkCanvas::rotate(SkScalar degrees) {
1122 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001123 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 return fMCRec->fMatrix->preRotate(degrees);
1125}
1126
1127bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1128 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001129 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 return fMCRec->fMatrix->preSkew(sx, sy);
1131}
1132
1133bool SkCanvas::concat(const SkMatrix& matrix) {
1134 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001135 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 return fMCRec->fMatrix->preConcat(matrix);
1137}
1138
1139void SkCanvas::setMatrix(const SkMatrix& matrix) {
1140 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001141 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 *fMCRec->fMatrix = matrix;
1143}
1144
1145// this is not virtual, so it must call a virtual method so that subclasses
1146// will see its action
1147void SkCanvas::resetMatrix() {
1148 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001149
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 matrix.reset();
1151 this->setMatrix(matrix);
1152}
1153
1154//////////////////////////////////////////////////////////////////////////////
1155
reed@google.comc42d35d2011-10-12 11:57:42 +00001156bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001157#ifdef SK_ENABLE_CLIP_QUICKREJECT
1158 if (SkRegion::kIntersect_Op == op) {
1159 if (fMCRec->fRasterClip->isEmpty()) {
1160 return false;
1161 }
1162
reed@google.com3b3e8952012-08-16 20:53:31 +00001163 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001164 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001165 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001166
1167 fClipStack.clipEmpty();
1168 return fMCRec->fRasterClip->setEmpty();
1169 }
1170 }
1171#endif
1172
reed@google.com5c3d1472011-02-22 19:12:23 +00001173 AutoValidateClip avc(this);
1174
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001176 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001177 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178
1179 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001180 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001181 // the matrix. This means we don't have to a) make a path, and b) tell
1182 // the region code to scan-convert the path, only to discover that it
1183 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185
1186 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001187 fClipStack.clipDevRect(r, op, doAA);
1188 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001190 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001191 // and clip against that, since it can handle any matrix. However, to
1192 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1193 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 SkPath path;
1195
1196 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001197 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 }
1199}
1200
reed@google.com00177082011-10-12 14:34:30 +00001201static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001202 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001203 // base is used to limit the size (and therefore memory allocation) of the
1204 // region that results from scan converting devPath.
1205 SkRegion base;
1206
reed@google.com819c9212011-02-23 18:56:55 +00001207 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001208 // since we are intersect, we can do better (tighter) with currRgn's
1209 // bounds, than just using the device. However, if currRgn is complex,
1210 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001211 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001212 // FIXME: we should also be able to do this when currClip->isBW(),
1213 // but relaxing the test above triggers GM asserts in
1214 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1215 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001216 } else {
reed@google.com00177082011-10-12 14:34:30 +00001217 base.setRect(currClip->getBounds());
1218 SkRasterClip clip;
1219 clip.setPath(devPath, base, doAA);
1220 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001221 }
reed@google.com819c9212011-02-23 18:56:55 +00001222 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001223 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001224 if (!device) {
1225 return currClip->setEmpty();
1226 }
1227
junov@chromium.orga907ac32012-02-24 21:54:07 +00001228 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001229
1230 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001231 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001232 } else {
reed@google.com00177082011-10-12 14:34:30 +00001233 SkRasterClip clip;
1234 clip.setPath(devPath, base, doAA);
1235 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001236 }
1237 }
1238}
1239
reed@google.com4ed0fb72012-12-12 20:48:18 +00001240bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1241 if (rrect.isRect()) {
1242 // call the non-virtual version
1243 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1244 } else {
1245 SkPath path;
1246 path.addRRect(rrect);
1247 // call the non-virtual version
1248 return this->SkCanvas::clipPath(path, op, doAA);
1249 }
1250}
1251
reed@google.comc42d35d2011-10-12 11:57:42 +00001252bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001253#ifdef SK_ENABLE_CLIP_QUICKREJECT
1254 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1255 if (fMCRec->fRasterClip->isEmpty()) {
1256 return false;
1257 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001258
reed@google.com3b3e8952012-08-16 20:53:31 +00001259 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001260 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001261 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001262
reed@google.comda17f752012-08-16 18:27:05 +00001263 fClipStack.clipEmpty();
1264 return fMCRec->fRasterClip->setEmpty();
1265 }
1266 }
1267#endif
1268
reed@google.com5c3d1472011-02-22 19:12:23 +00001269 AutoValidateClip avc(this);
1270
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001272 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001273 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274
1275 SkPath devPath;
1276 path.transform(*fMCRec->fMatrix, &devPath);
1277
reed@google.comfe701122011-11-08 19:41:23 +00001278 // Check if the transfomation, or the original path itself
1279 // made us empty. Note this can also happen if we contained NaN
1280 // values. computing the bounds detects this, and will set our
1281 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1282 if (devPath.getBounds().isEmpty()) {
1283 // resetting the path will remove any NaN or other wanky values
1284 // that might upset our scan converter.
1285 devPath.reset();
1286 }
1287
reed@google.com5c3d1472011-02-22 19:12:23 +00001288 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001289 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001290
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001291 if (fAllowSimplifyClip) {
1292 devPath.reset();
1293 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1294 const SkClipStack* clipStack = getClipStack();
1295 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1296 const SkClipStack::Element* element;
1297 while ((element = iter.next())) {
1298 SkClipStack::Element::Type type = element->getType();
1299 if (type == SkClipStack::Element::kEmpty_Type) {
1300 continue;
1301 }
1302 SkPath operand;
1303 if (type == SkClipStack::Element::kRect_Type) {
1304 operand.addRect(element->getRect());
1305 } else if (type == SkClipStack::Element::kPath_Type) {
1306 operand = element->getPath();
1307 } else {
1308 SkDEBUGFAIL("Unexpected type.");
1309 }
1310 SkRegion::Op elementOp = element->getOp();
1311 if (elementOp == SkRegion::kReplace_Op) {
1312 devPath = operand;
1313 } else {
1314 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1315 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001316 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1317 // perhaps we need an API change to avoid this sort of mixed-signals about
1318 // clipping.
1319 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001320 }
1321 op = SkRegion::kReplace_Op;
1322 }
1323
reed@google.com00177082011-10-12 14:34:30 +00001324 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325}
1326
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001327bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1328 bool inverseFilled) {
1329 // This is for updating the clip conservatively using only bounds
1330 // information.
1331 // Contract:
1332 // The current clip must contain the true clip. The true
1333 // clip is the clip that would have normally been computed
1334 // by calls to clipPath and clipRRect
1335 // Objective:
1336 // Keep the current clip as small as possible without
1337 // breaking the contract, using only clip bounding rectangles
1338 // (for performance).
1339
1340 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1341 // don't have to worry about getting caught in a loop. Thus anywhere
1342 // we call a virtual method, we explicitly prefix it with
1343 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001344
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001345 if (inverseFilled) {
1346 switch (op) {
1347 case SkRegion::kIntersect_Op:
1348 case SkRegion::kDifference_Op:
1349 // These ops can only shrink the current clip. So leaving
1350 // the clip unchanges conservatively respects the contract.
1351 return this->getClipDeviceBounds(NULL);
1352 case SkRegion::kUnion_Op:
1353 case SkRegion::kReplace_Op:
1354 case SkRegion::kReverseDifference_Op:
1355 case SkRegion::kXOR_Op:
1356 {
1357 // These ops can grow the current clip up to the extents of
1358 // the input clip, which is inverse filled, so we just set
1359 // the current clip to the device bounds.
1360 SkRect deviceBounds;
1361 SkIRect deviceIBounds;
1362 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001363 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001364 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1365 // set the clip in device space
1366 this->SkCanvas::setMatrix(SkMatrix::I());
1367 bool result = this->SkCanvas::clipRect(deviceBounds,
1368 SkRegion::kReplace_Op, false);
1369 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1370 return result;
1371 }
1372 default:
1373 SkASSERT(0); // unhandled op?
1374 }
1375 } else {
1376 // Not inverse filled
1377 switch (op) {
1378 case SkRegion::kIntersect_Op:
1379 case SkRegion::kUnion_Op:
1380 case SkRegion::kReplace_Op:
1381 return this->SkCanvas::clipRect(bounds, op, false);
1382 case SkRegion::kDifference_Op:
1383 // Difference can only shrink the current clip.
1384 // Leaving clip unchanged conservatively fullfills the contract.
1385 return this->getClipDeviceBounds(NULL);
1386 case SkRegion::kReverseDifference_Op:
1387 // To reverse, we swap in the bounds with a replace op.
1388 // As with difference, leave it unchanged.
1389 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1390 case SkRegion::kXOR_Op:
1391 // Be conservative, based on (A XOR B) always included in (A union B),
1392 // which is always included in (bounds(A) union bounds(B))
1393 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1394 default:
1395 SkASSERT(0); // unhandled op?
1396 }
1397 }
1398 return true;
1399}
1400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001402 AutoValidateClip avc(this);
1403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001405 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
reed@google.com5c3d1472011-02-22 19:12:23 +00001407 // todo: signal fClipStack that we have a region, and therefore (I guess)
1408 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001409 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001410
reed@google.com00177082011-10-12 14:34:30 +00001411 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412}
1413
reed@google.com819c9212011-02-23 18:56:55 +00001414#ifdef SK_DEBUG
1415void SkCanvas::validateClip() const {
1416 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001417 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001418 if (!device) {
1419 SkASSERT(this->getTotalClip().isEmpty());
1420 return;
1421 }
1422
reed@google.com819c9212011-02-23 18:56:55 +00001423 SkIRect ir;
1424 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001425 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001426
robertphillips@google.com80214e22012-07-20 15:33:18 +00001427 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001428 const SkClipStack::Element* element;
1429 while ((element = iter.next()) != NULL) {
1430 switch (element->getType()) {
1431 case SkClipStack::Element::kPath_Type:
1432 clipPathHelper(this,
1433 &tmpClip,
1434 element->getPath(),
1435 element->getOp(),
1436 element->isAA());
1437 break;
1438 case SkClipStack::Element::kRect_Type:
1439 element->getRect().round(&ir);
1440 tmpClip.op(ir, element->getOp());
1441 break;
1442 case SkClipStack::Element::kEmpty_Type:
1443 tmpClip.setEmpty();
1444 break;
reed@google.com819c9212011-02-23 18:56:55 +00001445 }
1446 }
1447
reed@google.com6f8f2922011-03-04 22:27:10 +00001448#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001449 // now compare against the current rgn
1450 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001451 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001452#endif
reed@google.com819c9212011-02-23 18:56:55 +00001453}
1454#endif
1455
reed@google.com90c07ea2012-04-13 13:50:27 +00001456void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001457 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001458 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001459
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001460 static const SkRect kEmpty = { 0, 0, 0, 0 };
1461 while ((element = iter.next()) != NULL) {
1462 switch (element->getType()) {
1463 case SkClipStack::Element::kPath_Type:
1464 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1465 break;
1466 case SkClipStack::Element::kRect_Type:
1467 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1468 break;
1469 case SkClipStack::Element::kEmpty_Type:
1470 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1471 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001472 }
1473 }
1474}
1475
reed@google.com5c3d1472011-02-22 19:12:23 +00001476///////////////////////////////////////////////////////////////////////////////
1477
reed@google.com3b3e8952012-08-16 20:53:31 +00001478bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001479
reed@google.com16078632011-12-06 18:56:37 +00001480 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001481 return true;
1482
reed@google.com00177082011-10-12 14:34:30 +00001483 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 return true;
1485 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486
tomhudson@google.com8d430182011-06-06 19:11:19 +00001487 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001488 SkRect dst;
1489 fMCRec->fMatrix->mapRect(&dst, rect);
1490 SkIRect idst;
1491 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001492 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001493 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001494 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001495
reed@android.coma380ae42009-07-21 01:17:02 +00001496 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001497 // TODO: should we use | instead, or compare all 4 at once?
1498 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001499 return true;
1500 }
reed@google.comc0784db2013-12-13 21:16:12 +00001501 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001502 return true;
1503 }
1504 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506}
1507
reed@google.com3b3e8952012-08-16 20:53:31 +00001508bool SkCanvas::quickReject(const SkPath& path) const {
1509 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510}
1511
reed@google.com3b3e8952012-08-16 20:53:31 +00001512bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001513 SkIRect ibounds;
1514 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515 return false;
1516 }
1517
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001518 SkMatrix inverse;
1519 // if we can't invert the CTM, we can't return local clip bounds
1520 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001521 if (bounds) {
1522 bounds->setEmpty();
1523 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001524 return false;
1525 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001527 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001528 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001529 // adjust it outwards in case we are antialiasing
1530 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001531
reed@google.com8f4d2302013-12-17 16:44:46 +00001532 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1533 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534 inverse.mapRect(bounds, r);
1535 }
1536 return true;
1537}
1538
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001539bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001540 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001541 if (clip.isEmpty()) {
1542 if (bounds) {
1543 bounds->setEmpty();
1544 }
1545 return false;
1546 }
1547
1548 if (NULL != bounds) {
1549 *bounds = clip.getBounds();
1550 }
1551 return true;
1552}
1553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554const SkMatrix& SkCanvas::getTotalMatrix() const {
1555 return *fMCRec->fMatrix;
1556}
1557
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001558SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001559 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1560 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001561 return kComplex_ClipType;
1562}
1563
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001565 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566}
1567
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001568SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001569 int width, int height,
1570 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001571 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001572 if (device) {
1573 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1574 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001575 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001576 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001577 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578}
1579
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001580GrContext* SkCanvas::getGrContext() {
1581#if SK_SUPPORT_GPU
1582 SkBaseDevice* device = this->getTopDevice();
1583 if (NULL != device) {
1584 GrRenderTarget* renderTarget = device->accessRenderTarget();
1585 if (NULL != renderTarget) {
1586 return renderTarget->getContext();
1587 }
1588 }
1589#endif
1590
1591 return NULL;
1592
1593}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001594
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595//////////////////////////////////////////////////////////////////////////////
1596// These are the virtual drawing methods
1597//////////////////////////////////////////////////////////////////////////////
1598
reed@google.com2a981812011-04-14 18:59:28 +00001599void SkCanvas::clear(SkColor color) {
1600 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001601 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001602 while (iter.next()) {
1603 iter.fDevice->clear(color);
1604 }
1605}
1606
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001608 this->internalDrawPaint(paint);
1609}
1610
1611void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001612 CHECK_SHADER_NOSETCONTEXT(paint);
1613
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001614 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615
1616 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001617 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618 }
1619
reed@google.com4e2b3d32011-04-07 14:18:59 +00001620 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621}
1622
1623void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1624 const SkPaint& paint) {
1625 if ((long)count <= 0) {
1626 return;
1627 }
1628
reed@google.comea033602012-12-14 13:13:55 +00001629 CHECK_SHADER_NOSETCONTEXT(paint);
1630
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001631 SkRect r, storage;
1632 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001633 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001634 // special-case 2 points (common for drawing a single line)
1635 if (2 == count) {
1636 r.set(pts[0], pts[1]);
1637 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001638 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001639 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001640 bounds = &paint.computeFastStrokeBounds(r, &storage);
1641 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001642 return;
1643 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001644 }
reed@google.coma584aed2012-05-16 14:06:02 +00001645
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646 SkASSERT(pts != NULL);
1647
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001648 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001649
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001651 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 }
reed@google.com4b226022011-01-11 18:32:13 +00001653
reed@google.com4e2b3d32011-04-07 14:18:59 +00001654 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655}
1656
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001657void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001658 CHECK_SHADER_NOSETCONTEXT(paint);
1659
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001660 SkRect storage;
1661 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001663 bounds = &paint.computeFastBounds(r, &storage);
1664 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665 return;
1666 }
1667 }
reed@google.com4b226022011-01-11 18:32:13 +00001668
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001669 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670
1671 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001672 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 }
1674
reed@google.com4e2b3d32011-04-07 14:18:59 +00001675 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676}
1677
reed@google.com4ed0fb72012-12-12 20:48:18 +00001678void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001679 CHECK_SHADER_NOSETCONTEXT(paint);
1680
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001681 SkRect storage;
1682 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001683 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001684 bounds = &paint.computeFastBounds(oval, &storage);
1685 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001686 return;
1687 }
1688 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001689
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001690 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001691
1692 while (iter.next()) {
1693 iter.fDevice->drawOval(iter, oval, looper.paint());
1694 }
1695
1696 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001697}
1698
1699void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001700 CHECK_SHADER_NOSETCONTEXT(paint);
1701
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001702 SkRect storage;
1703 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001704 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001705 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1706 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001707 return;
1708 }
1709 }
1710
1711 if (rrect.isRect()) {
1712 // call the non-virtual version
1713 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001714 return;
1715 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001716 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001717 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1718 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001719 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001720
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001721 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001722
1723 while (iter.next()) {
1724 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1725 }
1726
1727 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001728}
1729
1730
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001731void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001732 CHECK_SHADER_NOSETCONTEXT(paint);
1733
reed@google.com93645112012-07-26 16:11:47 +00001734 if (!path.isFinite()) {
1735 return;
1736 }
1737
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001738 SkRect storage;
1739 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001740 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001741 const SkRect& pathBounds = path.getBounds();
1742 bounds = &paint.computeFastBounds(pathBounds, &storage);
1743 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 return;
1745 }
1746 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001747 if (path.isEmpty()) {
1748 if (path.isInverseFillType()) {
1749 this->internalDrawPaint(paint);
1750 }
1751 return;
1752 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001754 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755
1756 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001757 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 }
1759
reed@google.com4e2b3d32011-04-07 14:18:59 +00001760 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761}
1762
1763void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1764 const SkPaint* paint) {
1765 SkDEBUGCODE(bitmap.validate();)
1766
reed@google.com3d608122011-11-21 15:16:16 +00001767 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001768 SkRect bounds = {
1769 x, y,
1770 x + SkIntToScalar(bitmap.width()),
1771 y + SkIntToScalar(bitmap.height())
1772 };
1773 if (paint) {
1774 (void)paint->computeFastBounds(bounds, &bounds);
1775 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001776 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 return;
1778 }
1779 }
reed@google.com4b226022011-01-11 18:32:13 +00001780
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 SkMatrix matrix;
1782 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001783 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784}
1785
reed@google.com9987ec32011-09-07 11:57:52 +00001786// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001787void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001788 const SkRect& dst, const SkPaint* paint,
1789 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1791 return;
1792 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001793
reed@google.comea033602012-12-14 13:13:55 +00001794 CHECK_LOCKCOUNT_BALANCE(bitmap);
1795
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 SkRect storage;
1797 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001798 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001799 if (paint) {
1800 bounds = &paint->computeFastBounds(dst, &storage);
1801 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001802 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001803 return;
1804 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805 }
reed@google.com3d608122011-11-21 15:16:16 +00001806
reed@google.com33535f32012-09-25 15:37:50 +00001807 SkLazyPaint lazy;
1808 if (NULL == paint) {
1809 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001811
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001812 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001813
reed@google.com33535f32012-09-25 15:37:50 +00001814 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001815 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001816 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001817
reed@google.com33535f32012-09-25 15:37:50 +00001818 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819}
1820
reed@google.com71121732012-09-18 15:14:33 +00001821void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001822 const SkRect& dst, const SkPaint* paint,
1823 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001824 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001825 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001826}
1827
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1829 const SkPaint* paint) {
1830 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001831 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832}
1833
reed@google.com9987ec32011-09-07 11:57:52 +00001834void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1835 const SkIRect& center, const SkRect& dst,
1836 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001837 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001838 SkRect storage;
1839 const SkRect* bounds = &dst;
1840 if (paint) {
1841 bounds = &paint->computeFastBounds(dst, &storage);
1842 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001843 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001844 return;
1845 }
1846 }
1847
reed@google.com9987ec32011-09-07 11:57:52 +00001848 const int32_t w = bitmap.width();
1849 const int32_t h = bitmap.height();
1850
1851 SkIRect c = center;
1852 // pin center to the bounds of the bitmap
1853 c.fLeft = SkMax32(0, center.fLeft);
1854 c.fTop = SkMax32(0, center.fTop);
1855 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1856 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1857
reed@google.com71121732012-09-18 15:14:33 +00001858 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001859 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001860 };
1861 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001862 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001863 };
reed@google.com9987ec32011-09-07 11:57:52 +00001864 SkScalar dstX[4] = {
1865 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1866 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1867 };
1868 SkScalar dstY[4] = {
1869 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1870 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1871 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001872
reed@google.com9987ec32011-09-07 11:57:52 +00001873 if (dstX[1] > dstX[2]) {
1874 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1875 dstX[2] = dstX[1];
1876 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001877
reed@google.com9987ec32011-09-07 11:57:52 +00001878 if (dstY[1] > dstY[2]) {
1879 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1880 dstY[2] = dstY[1];
1881 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001882
reed@google.com9987ec32011-09-07 11:57:52 +00001883 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001884 SkRect s, d;
1885
reed@google.com9987ec32011-09-07 11:57:52 +00001886 s.fTop = srcY[y];
1887 s.fBottom = srcY[y+1];
1888 d.fTop = dstY[y];
1889 d.fBottom = dstY[y+1];
1890 for (int x = 0; x < 3; x++) {
1891 s.fLeft = srcX[x];
1892 s.fRight = srcX[x+1];
1893 d.fLeft = dstX[x];
1894 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001895 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001896 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001897 }
1898 }
1899}
1900
1901void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1902 const SkRect& dst, const SkPaint* paint) {
1903 SkDEBUGCODE(bitmap.validate();)
1904
1905 // Need a device entry-point, so gpu can use a mesh
1906 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1907}
1908
reed@google.comf67e4cf2011-03-15 20:56:58 +00001909class SkDeviceFilteredPaint {
1910public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001911 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1912 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001913 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001914 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001915 newPaint->setFlags(flags.fFlags);
1916 newPaint->setHinting(flags.fHinting);
1917 fPaint = newPaint;
1918 } else {
1919 fPaint = &paint;
1920 }
1921 }
1922
reed@google.comf67e4cf2011-03-15 20:56:58 +00001923 const SkPaint& paint() const { return *fPaint; }
1924
1925private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001926 const SkPaint* fPaint;
1927 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001928};
1929
bungeman@google.com52c748b2011-08-22 21:30:43 +00001930void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1931 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001932 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001933 draw.fDevice->drawRect(draw, r, paint);
1934 } else {
1935 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001936 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001937 draw.fDevice->drawRect(draw, r, p);
1938 }
1939}
1940
1941void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1942 const char text[], size_t byteLength,
1943 SkScalar x, SkScalar y) {
1944 SkASSERT(byteLength == 0 || text != NULL);
1945
1946 // nothing to draw
1947 if (text == NULL || byteLength == 0 ||
1948 draw.fClip->isEmpty() ||
1949 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1950 return;
1951 }
1952
1953 SkScalar width = 0;
1954 SkPoint start;
1955
1956 start.set(0, 0); // to avoid warning
1957 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1958 SkPaint::kStrikeThruText_Flag)) {
1959 width = paint.measureText(text, byteLength);
1960
1961 SkScalar offsetX = 0;
1962 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1963 offsetX = SkScalarHalf(width);
1964 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1965 offsetX = width;
1966 }
1967 start.set(x - offsetX, y);
1968 }
1969
1970 if (0 == width) {
1971 return;
1972 }
1973
1974 uint32_t flags = paint.getFlags();
1975
1976 if (flags & (SkPaint::kUnderlineText_Flag |
1977 SkPaint::kStrikeThruText_Flag)) {
1978 SkScalar textSize = paint.getTextSize();
1979 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1980 SkRect r;
1981
1982 r.fLeft = start.fX;
1983 r.fRight = start.fX + width;
1984
1985 if (flags & SkPaint::kUnderlineText_Flag) {
1986 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1987 start.fY);
1988 r.fTop = offset;
1989 r.fBottom = offset + height;
1990 DrawRect(draw, paint, r, textSize);
1991 }
1992 if (flags & SkPaint::kStrikeThruText_Flag) {
1993 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1994 start.fY);
1995 r.fTop = offset;
1996 r.fBottom = offset + height;
1997 DrawRect(draw, paint, r, textSize);
1998 }
1999 }
2000}
2001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002void SkCanvas::drawText(const void* text, size_t byteLength,
2003 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002004 CHECK_SHADER_NOSETCONTEXT(paint);
2005
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002006 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007
2008 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002009 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002010 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002011 DrawTextDecorations(iter, dfp.paint(),
2012 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002013 }
2014
reed@google.com4e2b3d32011-04-07 14:18:59 +00002015 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016}
2017
2018void SkCanvas::drawPosText(const void* text, size_t byteLength,
2019 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002020 CHECK_SHADER_NOSETCONTEXT(paint);
2021
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002022 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002023
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002025 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002027 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 }
reed@google.com4b226022011-01-11 18:32:13 +00002029
reed@google.com4e2b3d32011-04-07 14:18:59 +00002030 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002031}
2032
2033void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2034 const SkScalar xpos[], SkScalar constY,
2035 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002036 CHECK_SHADER_NOSETCONTEXT(paint);
2037
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002038 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002039
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002041 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002043 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 }
reed@google.com4b226022011-01-11 18:32:13 +00002045
reed@google.com4e2b3d32011-04-07 14:18:59 +00002046 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047}
2048
2049void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2050 const SkPath& path, const SkMatrix* matrix,
2051 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002052 CHECK_SHADER_NOSETCONTEXT(paint);
2053
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002054 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055
2056 while (iter.next()) {
2057 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002058 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059 }
2060
reed@google.com4e2b3d32011-04-07 14:18:59 +00002061 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062}
2063
2064void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2065 const SkPoint verts[], const SkPoint texs[],
2066 const SkColor colors[], SkXfermode* xmode,
2067 const uint16_t indices[], int indexCount,
2068 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002069 CHECK_SHADER_NOSETCONTEXT(paint);
2070
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002071 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002072
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 while (iter.next()) {
2074 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002075 colors, xmode, indices, indexCount,
2076 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077 }
reed@google.com4b226022011-01-11 18:32:13 +00002078
reed@google.com4e2b3d32011-04-07 14:18:59 +00002079 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080}
2081
2082//////////////////////////////////////////////////////////////////////////////
2083// These methods are NOT virtual, and therefore must call back into virtual
2084// methods, rather than actually drawing themselves.
2085//////////////////////////////////////////////////////////////////////////////
2086
2087void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002088 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 SkPaint paint;
2090
2091 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002092 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002093 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094 }
2095 this->drawPaint(paint);
2096}
2097
reed@android.com845fdac2009-06-23 03:01:32 +00002098void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 SkPaint paint;
2100
2101 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002102 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002103 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104 }
2105 this->drawPaint(paint);
2106}
2107
2108void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2109 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002110
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 pt.set(x, y);
2112 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2113}
2114
2115void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2116 SkPoint pt;
2117 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002118
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119 pt.set(x, y);
2120 paint.setColor(color);
2121 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2122}
2123
2124void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2125 const SkPaint& paint) {
2126 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002127
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128 pts[0].set(x0, y0);
2129 pts[1].set(x1, y1);
2130 this->drawPoints(kLines_PointMode, 2, pts, paint);
2131}
2132
2133void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2134 SkScalar right, SkScalar bottom,
2135 const SkPaint& paint) {
2136 SkRect r;
2137
2138 r.set(left, top, right, bottom);
2139 this->drawRect(r, paint);
2140}
2141
2142void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2143 const SkPaint& paint) {
2144 if (radius < 0) {
2145 radius = 0;
2146 }
2147
2148 SkRect r;
2149 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002150 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151}
2152
2153void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2154 const SkPaint& paint) {
2155 if (rx > 0 && ry > 0) {
2156 if (paint.canComputeFastBounds()) {
2157 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002158 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 return;
2160 }
2161 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002162 SkRRect rrect;
2163 rrect.setRectXY(r, rx, ry);
2164 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165 } else {
2166 this->drawRect(r, paint);
2167 }
2168}
2169
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2171 SkScalar sweepAngle, bool useCenter,
2172 const SkPaint& paint) {
2173 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2174 this->drawOval(oval, paint);
2175 } else {
2176 SkPath path;
2177 if (useCenter) {
2178 path.moveTo(oval.centerX(), oval.centerY());
2179 }
2180 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2181 if (useCenter) {
2182 path.close();
2183 }
2184 this->drawPath(path, paint);
2185 }
2186}
2187
2188void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2189 const SkPath& path, SkScalar hOffset,
2190 SkScalar vOffset, const SkPaint& paint) {
2191 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002192
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193 matrix.setTranslate(hOffset, vOffset);
2194 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2195}
2196
reed@android.comf76bacf2009-05-13 14:00:33 +00002197///////////////////////////////////////////////////////////////////////////////
2198
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201}
2202
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203///////////////////////////////////////////////////////////////////////////////
2204///////////////////////////////////////////////////////////////////////////////
2205
2206SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002207 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208
2209 SkASSERT(canvas);
2210
2211 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2212 fDone = !fImpl->next();
2213}
2214
2215SkCanvas::LayerIter::~LayerIter() {
2216 fImpl->~SkDrawIter();
2217}
2218
2219void SkCanvas::LayerIter::next() {
2220 fDone = !fImpl->next();
2221}
2222
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002223SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224 return fImpl->getDevice();
2225}
2226
2227const SkMatrix& SkCanvas::LayerIter::matrix() const {
2228 return fImpl->getMatrix();
2229}
2230
2231const SkPaint& SkCanvas::LayerIter::paint() const {
2232 const SkPaint* paint = fImpl->getPaint();
2233 if (NULL == paint) {
2234 paint = &fDefaultPaint;
2235 }
2236 return *paint;
2237}
2238
2239const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2240int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2241int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002242
2243///////////////////////////////////////////////////////////////////////////////
2244
2245SkCanvas::ClipVisitor::~ClipVisitor() { }