blob: 08b09e8e2b6e970b06d93c0449b9af8859573173 [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
835int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
836 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000837 // do this before we create the layer. We don't call the public save() since
838 // that would invoke a possibly overridden virtual
839 int count = this->internalSave(flags);
840
841 fDeviceCMDirty = true;
842
843 SkIRect ir;
844 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 return count;
846 }
847
reed@google.comb55deeb2012-01-06 14:43:09 +0000848 // Kill the imagefilter if our device doesn't allow it
849 SkLazyPaint lazyP;
850 if (paint && paint->getImageFilter()) {
851 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000852 if (justForImageFilter) {
853 // early exit if the layer was just for the imageFilter
854 return count;
855 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000856 SkPaint* p = lazyP.set(*paint);
857 p->setImageFilter(NULL);
858 paint = p;
859 }
860 }
861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 bool isOpaque;
863 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
864
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000865 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000866 if (paint && paint->getImageFilter()) {
867 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
868 isOpaque);
869 } else {
870 device = this->createLayerDevice(config, ir.width(), ir.height(),
871 isOpaque);
872 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000873 if (NULL == device) {
874 SkDebugf("Unable to create device for layer.");
875 return count;
876 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000877
reed@google.com6f8f2922011-03-04 22:27:10 +0000878 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000879 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 device->unref();
881
882 layer->fNext = fMCRec->fTopLayer;
883 fMCRec->fLayer = layer;
884 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
885
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000886 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 return count;
888}
889
890int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
891 SaveFlags flags) {
892 if (0xFF == alpha) {
893 return this->saveLayer(bounds, NULL, flags);
894 } else {
895 SkPaint tmpPaint;
896 tmpPaint.setAlpha(alpha);
897 return this->saveLayer(bounds, &tmpPaint, flags);
898 }
899}
900
901void SkCanvas::restore() {
902 // check for underflow
903 if (fMCStack.count() > 1) {
904 this->internalRestore();
905 }
906}
907
908void SkCanvas::internalRestore() {
909 SkASSERT(fMCStack.count() != 0);
910
911 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000912 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000914 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
915 fClipStack.restore();
916 }
917
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000918 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 DeviceCM* layer = fMCRec->fLayer; // may be null
920 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
921 fMCRec->fLayer = NULL;
922
923 // now do the normal restore()
924 fMCRec->~MCRec(); // balanced in save()
925 fMCStack.pop_back();
926 fMCRec = (MCRec*)fMCStack.back();
927
928 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
929 since if we're being recorded, we don't want to record this (the
930 recorder will have already recorded the restore).
931 */
932 if (NULL != layer) {
933 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000934 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000935 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
936 layer->fPaint);
937 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000939
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000940 SkASSERT(fSaveLayerCount > 0);
941 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 }
943 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000944 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945}
946
947int SkCanvas::getSaveCount() const {
948 return fMCStack.count();
949}
950
951void SkCanvas::restoreToCount(int count) {
952 // sanity check
953 if (count < 1) {
954 count = 1;
955 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000956
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000957 int n = this->getSaveCount() - count;
958 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 this->restore();
960 }
961}
962
reed@google.com7c202932011-12-14 18:48:05 +0000963bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000964 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000965}
966
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967/////////////////////////////////////////////////////////////////////////////
968
969// can't draw it if its empty, or its too big for a fixed-point width or height
970static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.com28534292013-07-23 22:03:26 +0000971 return bitmap.width() <= 0 || bitmap.height() <= 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972}
973
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000974void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 const SkMatrix& matrix, const SkPaint* paint) {
976 if (reject_bitmap(bitmap)) {
977 return;
978 }
979
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000980 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000982 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000984
985 SkDEBUGCODE(bitmap.validate();)
986 CHECK_LOCKCOUNT_BALANCE(bitmap);
987
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000988 SkRect storage;
989 const SkRect* bounds = NULL;
990 if (paint && paint->canComputeFastBounds()) {
991 bitmap.getBounds(&storage);
992 matrix.mapRect(&storage);
993 bounds = &paint->computeFastBounds(storage, &storage);
994 }
995
996 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +0000997
998 while (iter.next()) {
999 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1000 }
1001
1002 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003}
1004
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001005void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001006 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 SkPaint tmp;
1008 if (NULL == paint) {
1009 tmp.setDither(true);
1010 paint = &tmp;
1011 }
reed@google.com4b226022011-01-11 18:32:13 +00001012
reed@google.com8926b162012-03-23 15:36:36 +00001013 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001015 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001016 paint = &looper.paint();
1017 SkImageFilter* filter = paint->getImageFilter();
1018 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001019 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001020 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001021 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001022 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001023 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001024 SkMatrix matrix = *iter.fMatrix;
1025 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001026 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001027 SkPaint tmpUnfiltered(*paint);
1028 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001029 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1030 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001031 }
1032 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001033 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001034 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001036 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037}
1038
reed@google.com8926b162012-03-23 15:36:36 +00001039void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1040 const SkPaint* paint) {
1041 SkDEBUGCODE(bitmap.validate();)
reed@google.comea033602012-12-14 13:13:55 +00001042 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001043
reed@google.com8926b162012-03-23 15:36:36 +00001044 if (reject_bitmap(bitmap)) {
1045 return;
1046 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001047
reed@google.com8926b162012-03-23 15:36:36 +00001048 SkPaint tmp;
1049 if (NULL == paint) {
1050 paint = &tmp;
1051 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001052
reed@google.com8926b162012-03-23 15:36:36 +00001053 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001054
reed@google.com8926b162012-03-23 15:36:36 +00001055 while (iter.next()) {
1056 paint = &looper.paint();
1057 SkImageFilter* filter = paint->getImageFilter();
1058 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1059 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001060 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001061 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001062 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001063 SkMatrix matrix = *iter.fMatrix;
1064 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001065 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001066 SkPaint tmpUnfiltered(*paint);
1067 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001068 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001069 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001070 }
1071 } else {
1072 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1073 }
1074 }
1075 LOOPER_END
1076}
1077
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078/////////////////////////////////////////////////////////////////////////////
1079
1080bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1081 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001082 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 return fMCRec->fMatrix->preTranslate(dx, dy);
1084}
1085
1086bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1087 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001088 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 return fMCRec->fMatrix->preScale(sx, sy);
1090}
1091
1092bool SkCanvas::rotate(SkScalar degrees) {
1093 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001094 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 return fMCRec->fMatrix->preRotate(degrees);
1096}
1097
1098bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1099 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001100 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 return fMCRec->fMatrix->preSkew(sx, sy);
1102}
1103
1104bool SkCanvas::concat(const SkMatrix& matrix) {
1105 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001106 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 return fMCRec->fMatrix->preConcat(matrix);
1108}
1109
1110void SkCanvas::setMatrix(const SkMatrix& matrix) {
1111 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001112 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 *fMCRec->fMatrix = matrix;
1114}
1115
1116// this is not virtual, so it must call a virtual method so that subclasses
1117// will see its action
1118void SkCanvas::resetMatrix() {
1119 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001120
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 matrix.reset();
1122 this->setMatrix(matrix);
1123}
1124
1125//////////////////////////////////////////////////////////////////////////////
1126
reed@google.comc42d35d2011-10-12 11:57:42 +00001127bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001128#ifdef SK_ENABLE_CLIP_QUICKREJECT
1129 if (SkRegion::kIntersect_Op == op) {
1130 if (fMCRec->fRasterClip->isEmpty()) {
1131 return false;
1132 }
1133
reed@google.com3b3e8952012-08-16 20:53:31 +00001134 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001135 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001136 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001137
1138 fClipStack.clipEmpty();
1139 return fMCRec->fRasterClip->setEmpty();
1140 }
1141 }
1142#endif
1143
reed@google.com5c3d1472011-02-22 19:12:23 +00001144 AutoValidateClip avc(this);
1145
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001147 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001148 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149
1150 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001151 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001152 // the matrix. This means we don't have to a) make a path, and b) tell
1153 // the region code to scan-convert the path, only to discover that it
1154 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156
1157 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001158 fClipStack.clipDevRect(r, op, doAA);
1159 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001161 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001162 // and clip against that, since it can handle any matrix. However, to
1163 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1164 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 SkPath path;
1166
1167 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001168 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 }
1170}
1171
reed@google.com00177082011-10-12 14:34:30 +00001172static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001173 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001174 // base is used to limit the size (and therefore memory allocation) of the
1175 // region that results from scan converting devPath.
1176 SkRegion base;
1177
reed@google.com819c9212011-02-23 18:56:55 +00001178 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001179 // since we are intersect, we can do better (tighter) with currRgn's
1180 // bounds, than just using the device. However, if currRgn is complex,
1181 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001182 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001183 // FIXME: we should also be able to do this when currClip->isBW(),
1184 // but relaxing the test above triggers GM asserts in
1185 // SkRgnBuilder::blitH(). We need to investigate what's going on.
1186 return currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001187 } else {
reed@google.com00177082011-10-12 14:34:30 +00001188 base.setRect(currClip->getBounds());
1189 SkRasterClip clip;
1190 clip.setPath(devPath, base, doAA);
1191 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001192 }
reed@google.com819c9212011-02-23 18:56:55 +00001193 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001194 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001195 if (!device) {
1196 return currClip->setEmpty();
1197 }
1198
junov@chromium.orga907ac32012-02-24 21:54:07 +00001199 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001200
1201 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001202 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001203 } else {
reed@google.com00177082011-10-12 14:34:30 +00001204 SkRasterClip clip;
1205 clip.setPath(devPath, base, doAA);
1206 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001207 }
1208 }
1209}
1210
reed@google.com4ed0fb72012-12-12 20:48:18 +00001211bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1212 if (rrect.isRect()) {
1213 // call the non-virtual version
1214 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1215 } else {
1216 SkPath path;
1217 path.addRRect(rrect);
1218 // call the non-virtual version
1219 return this->SkCanvas::clipPath(path, op, doAA);
1220 }
1221}
1222
reed@google.comc42d35d2011-10-12 11:57:42 +00001223bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001224#ifdef SK_ENABLE_CLIP_QUICKREJECT
1225 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1226 if (fMCRec->fRasterClip->isEmpty()) {
1227 return false;
1228 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001229
reed@google.com3b3e8952012-08-16 20:53:31 +00001230 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001231 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001232 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001233
reed@google.comda17f752012-08-16 18:27:05 +00001234 fClipStack.clipEmpty();
1235 return fMCRec->fRasterClip->setEmpty();
1236 }
1237 }
1238#endif
1239
reed@google.com5c3d1472011-02-22 19:12:23 +00001240 AutoValidateClip avc(this);
1241
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001243 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001244 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245
1246 SkPath devPath;
1247 path.transform(*fMCRec->fMatrix, &devPath);
1248
reed@google.comfe701122011-11-08 19:41:23 +00001249 // Check if the transfomation, or the original path itself
1250 // made us empty. Note this can also happen if we contained NaN
1251 // values. computing the bounds detects this, and will set our
1252 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1253 if (devPath.getBounds().isEmpty()) {
1254 // resetting the path will remove any NaN or other wanky values
1255 // that might upset our scan converter.
1256 devPath.reset();
1257 }
1258
reed@google.com5c3d1472011-02-22 19:12:23 +00001259 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001260 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001261
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001262 if (fAllowSimplifyClip) {
1263 devPath.reset();
1264 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1265 const SkClipStack* clipStack = getClipStack();
1266 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1267 const SkClipStack::Element* element;
1268 while ((element = iter.next())) {
1269 SkClipStack::Element::Type type = element->getType();
1270 if (type == SkClipStack::Element::kEmpty_Type) {
1271 continue;
1272 }
1273 SkPath operand;
1274 if (type == SkClipStack::Element::kRect_Type) {
1275 operand.addRect(element->getRect());
1276 } else if (type == SkClipStack::Element::kPath_Type) {
1277 operand = element->getPath();
1278 } else {
1279 SkDEBUGFAIL("Unexpected type.");
1280 }
1281 SkRegion::Op elementOp = element->getOp();
1282 if (elementOp == SkRegion::kReplace_Op) {
1283 devPath = operand;
1284 } else {
1285 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1286 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001287 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1288 // perhaps we need an API change to avoid this sort of mixed-signals about
1289 // clipping.
1290 doAA |= element->isAA();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001291 }
1292 op = SkRegion::kReplace_Op;
1293 }
1294
reed@google.com00177082011-10-12 14:34:30 +00001295 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296}
1297
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001298bool SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
1299 bool inverseFilled) {
1300 // This is for updating the clip conservatively using only bounds
1301 // information.
1302 // Contract:
1303 // The current clip must contain the true clip. The true
1304 // clip is the clip that would have normally been computed
1305 // by calls to clipPath and clipRRect
1306 // Objective:
1307 // Keep the current clip as small as possible without
1308 // breaking the contract, using only clip bounding rectangles
1309 // (for performance).
1310
1311 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1312 // don't have to worry about getting caught in a loop. Thus anywhere
1313 // we call a virtual method, we explicitly prefix it with
1314 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001315
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001316 if (inverseFilled) {
1317 switch (op) {
1318 case SkRegion::kIntersect_Op:
1319 case SkRegion::kDifference_Op:
1320 // These ops can only shrink the current clip. So leaving
1321 // the clip unchanges conservatively respects the contract.
1322 return this->getClipDeviceBounds(NULL);
1323 case SkRegion::kUnion_Op:
1324 case SkRegion::kReplace_Op:
1325 case SkRegion::kReverseDifference_Op:
1326 case SkRegion::kXOR_Op:
1327 {
1328 // These ops can grow the current clip up to the extents of
1329 // the input clip, which is inverse filled, so we just set
1330 // the current clip to the device bounds.
1331 SkRect deviceBounds;
1332 SkIRect deviceIBounds;
1333 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001334 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001335 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1336 // set the clip in device space
1337 this->SkCanvas::setMatrix(SkMatrix::I());
1338 bool result = this->SkCanvas::clipRect(deviceBounds,
1339 SkRegion::kReplace_Op, false);
1340 this->SkCanvas::restore(); //pop the matrix, but keep the clip
1341 return result;
1342 }
1343 default:
1344 SkASSERT(0); // unhandled op?
1345 }
1346 } else {
1347 // Not inverse filled
1348 switch (op) {
1349 case SkRegion::kIntersect_Op:
1350 case SkRegion::kUnion_Op:
1351 case SkRegion::kReplace_Op:
1352 return this->SkCanvas::clipRect(bounds, op, false);
1353 case SkRegion::kDifference_Op:
1354 // Difference can only shrink the current clip.
1355 // Leaving clip unchanged conservatively fullfills the contract.
1356 return this->getClipDeviceBounds(NULL);
1357 case SkRegion::kReverseDifference_Op:
1358 // To reverse, we swap in the bounds with a replace op.
1359 // As with difference, leave it unchanged.
1360 return this->SkCanvas::clipRect(bounds, SkRegion::kReplace_Op, false);
1361 case SkRegion::kXOR_Op:
1362 // Be conservative, based on (A XOR B) always included in (A union B),
1363 // which is always included in (bounds(A) union bounds(B))
1364 return this->SkCanvas::clipRect(bounds, SkRegion::kUnion_Op, false);
1365 default:
1366 SkASSERT(0); // unhandled op?
1367 }
1368 }
1369 return true;
1370}
1371
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001373 AutoValidateClip avc(this);
1374
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001376 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377
reed@google.com5c3d1472011-02-22 19:12:23 +00001378 // todo: signal fClipStack that we have a region, and therefore (I guess)
1379 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001380 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001381
reed@google.com00177082011-10-12 14:34:30 +00001382 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383}
1384
reed@google.com819c9212011-02-23 18:56:55 +00001385#ifdef SK_DEBUG
1386void SkCanvas::validateClip() const {
1387 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001388 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001389 if (!device) {
1390 SkASSERT(this->getTotalClip().isEmpty());
1391 return;
1392 }
1393
reed@google.com819c9212011-02-23 18:56:55 +00001394 SkIRect ir;
1395 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001396 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001397
robertphillips@google.com80214e22012-07-20 15:33:18 +00001398 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001399 const SkClipStack::Element* element;
1400 while ((element = iter.next()) != NULL) {
1401 switch (element->getType()) {
1402 case SkClipStack::Element::kPath_Type:
1403 clipPathHelper(this,
1404 &tmpClip,
1405 element->getPath(),
1406 element->getOp(),
1407 element->isAA());
1408 break;
1409 case SkClipStack::Element::kRect_Type:
1410 element->getRect().round(&ir);
1411 tmpClip.op(ir, element->getOp());
1412 break;
1413 case SkClipStack::Element::kEmpty_Type:
1414 tmpClip.setEmpty();
1415 break;
reed@google.com819c9212011-02-23 18:56:55 +00001416 }
1417 }
1418
reed@google.com6f8f2922011-03-04 22:27:10 +00001419#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001420 // now compare against the current rgn
1421 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001422 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001423#endif
reed@google.com819c9212011-02-23 18:56:55 +00001424}
1425#endif
1426
reed@google.com90c07ea2012-04-13 13:50:27 +00001427void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001428 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001429 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001430
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001431 static const SkRect kEmpty = { 0, 0, 0, 0 };
1432 while ((element = iter.next()) != NULL) {
1433 switch (element->getType()) {
1434 case SkClipStack::Element::kPath_Type:
1435 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1436 break;
1437 case SkClipStack::Element::kRect_Type:
1438 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1439 break;
1440 case SkClipStack::Element::kEmpty_Type:
1441 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1442 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001443 }
1444 }
1445}
1446
reed@google.com5c3d1472011-02-22 19:12:23 +00001447///////////////////////////////////////////////////////////////////////////////
1448
reed@google.com3b3e8952012-08-16 20:53:31 +00001449bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001450
reed@google.com16078632011-12-06 18:56:37 +00001451 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001452 return true;
1453
reed@google.com00177082011-10-12 14:34:30 +00001454 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 return true;
1456 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457
tomhudson@google.com8d430182011-06-06 19:11:19 +00001458 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001459 SkRect dst;
1460 fMCRec->fMatrix->mapRect(&dst, rect);
1461 SkIRect idst;
1462 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001463 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001464 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001465 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001466
reed@android.coma380ae42009-07-21 01:17:02 +00001467 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001468 // TODO: should we use | instead, or compare all 4 at once?
1469 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001470 return true;
1471 }
reed@google.comc0784db2013-12-13 21:16:12 +00001472 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001473 return true;
1474 }
1475 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
reed@google.com3b3e8952012-08-16 20:53:31 +00001479bool SkCanvas::quickReject(const SkPath& path) const {
1480 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481}
1482
reed@google.com3b3e8952012-08-16 20:53:31 +00001483bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001484 SkIRect ibounds;
1485 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486 return false;
1487 }
1488
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001489 SkMatrix inverse;
1490 // if we can't invert the CTM, we can't return local clip bounds
1491 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001492 if (bounds) {
1493 bounds->setEmpty();
1494 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001495 return false;
1496 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001498 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001499 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001500 // adjust it outwards in case we are antialiasing
1501 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001502
reed@google.com8f4d2302013-12-17 16:44:46 +00001503 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1504 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 inverse.mapRect(bounds, r);
1506 }
1507 return true;
1508}
1509
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001510bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001511 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001512 if (clip.isEmpty()) {
1513 if (bounds) {
1514 bounds->setEmpty();
1515 }
1516 return false;
1517 }
1518
1519 if (NULL != bounds) {
1520 *bounds = clip.getBounds();
1521 }
1522 return true;
1523}
1524
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525const SkMatrix& SkCanvas::getTotalMatrix() const {
1526 return *fMCRec->fMatrix;
1527}
1528
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001529SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001530 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1531 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001532 return kComplex_ClipType;
1533}
1534
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001536 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537}
1538
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001539SkBaseDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001540 int width, int height,
1541 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001542 SkBaseDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001543 if (device) {
1544 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1545 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001546 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001547 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001548 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549}
1550
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001551SkBaseDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001552 int width, int height,
1553 bool isOpaque) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001554 SkBaseDevice* device = this->getDevice();
bsalomon@google.come97f0852011-06-17 13:10:25 +00001555 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001556 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001557 } else {
1558 return NULL;
1559 }
1560}
1561
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001562GrContext* SkCanvas::getGrContext() {
1563#if SK_SUPPORT_GPU
1564 SkBaseDevice* device = this->getTopDevice();
1565 if (NULL != device) {
1566 GrRenderTarget* renderTarget = device->accessRenderTarget();
1567 if (NULL != renderTarget) {
1568 return renderTarget->getContext();
1569 }
1570 }
1571#endif
1572
1573 return NULL;
1574
1575}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001576
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577//////////////////////////////////////////////////////////////////////////////
1578// These are the virtual drawing methods
1579//////////////////////////////////////////////////////////////////////////////
1580
reed@google.com2a981812011-04-14 18:59:28 +00001581void SkCanvas::clear(SkColor color) {
1582 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001583 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001584 while (iter.next()) {
1585 iter.fDevice->clear(color);
1586 }
1587}
1588
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001590 this->internalDrawPaint(paint);
1591}
1592
1593void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001594 CHECK_SHADER_NOSETCONTEXT(paint);
1595
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001596 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597
1598 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001599 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600 }
1601
reed@google.com4e2b3d32011-04-07 14:18:59 +00001602 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603}
1604
1605void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1606 const SkPaint& paint) {
1607 if ((long)count <= 0) {
1608 return;
1609 }
1610
reed@google.comea033602012-12-14 13:13:55 +00001611 CHECK_SHADER_NOSETCONTEXT(paint);
1612
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001613 SkRect r, storage;
1614 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001615 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001616 // special-case 2 points (common for drawing a single line)
1617 if (2 == count) {
1618 r.set(pts[0], pts[1]);
1619 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001620 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001621 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001622 bounds = &paint.computeFastStrokeBounds(r, &storage);
1623 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001624 return;
1625 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001626 }
reed@google.coma584aed2012-05-16 14:06:02 +00001627
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628 SkASSERT(pts != NULL);
1629
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001630 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001631
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001633 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 }
reed@google.com4b226022011-01-11 18:32:13 +00001635
reed@google.com4e2b3d32011-04-07 14:18:59 +00001636 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637}
1638
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001639void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001640 CHECK_SHADER_NOSETCONTEXT(paint);
1641
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001642 SkRect storage;
1643 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001645 bounds = &paint.computeFastBounds(r, &storage);
1646 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647 return;
1648 }
1649 }
reed@google.com4b226022011-01-11 18:32:13 +00001650
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001651 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652
1653 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001654 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 }
1656
reed@google.com4e2b3d32011-04-07 14:18:59 +00001657 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658}
1659
reed@google.com4ed0fb72012-12-12 20:48:18 +00001660void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001661 CHECK_SHADER_NOSETCONTEXT(paint);
1662
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001663 SkRect storage;
1664 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001665 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001666 bounds = &paint.computeFastBounds(oval, &storage);
1667 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001668 return;
1669 }
1670 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001671
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001672 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001673
1674 while (iter.next()) {
1675 iter.fDevice->drawOval(iter, oval, looper.paint());
1676 }
1677
1678 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001679}
1680
1681void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001682 CHECK_SHADER_NOSETCONTEXT(paint);
1683
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001684 SkRect storage;
1685 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001686 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001687 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1688 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001689 return;
1690 }
1691 }
1692
1693 if (rrect.isRect()) {
1694 // call the non-virtual version
1695 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001696 return;
1697 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001698 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001699 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1700 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001701 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001702
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001703 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001704
1705 while (iter.next()) {
1706 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1707 }
1708
1709 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001710}
1711
1712
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001713void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001714 CHECK_SHADER_NOSETCONTEXT(paint);
1715
reed@google.com93645112012-07-26 16:11:47 +00001716 if (!path.isFinite()) {
1717 return;
1718 }
1719
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001720 SkRect storage;
1721 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001722 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001723 const SkRect& pathBounds = path.getBounds();
1724 bounds = &paint.computeFastBounds(pathBounds, &storage);
1725 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726 return;
1727 }
1728 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001729 if (path.isEmpty()) {
1730 if (path.isInverseFillType()) {
1731 this->internalDrawPaint(paint);
1732 }
1733 return;
1734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001736 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737
1738 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001739 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740 }
1741
reed@google.com4e2b3d32011-04-07 14:18:59 +00001742 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743}
1744
1745void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1746 const SkPaint* paint) {
1747 SkDEBUGCODE(bitmap.validate();)
1748
reed@google.com3d608122011-11-21 15:16:16 +00001749 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001750 SkRect bounds = {
1751 x, y,
1752 x + SkIntToScalar(bitmap.width()),
1753 y + SkIntToScalar(bitmap.height())
1754 };
1755 if (paint) {
1756 (void)paint->computeFastBounds(bounds, &bounds);
1757 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001758 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759 return;
1760 }
1761 }
reed@google.com4b226022011-01-11 18:32:13 +00001762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 SkMatrix matrix;
1764 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001765 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766}
1767
reed@google.com9987ec32011-09-07 11:57:52 +00001768// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001769void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001770 const SkRect& dst, const SkPaint* paint,
1771 DrawBitmapRectFlags flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1773 return;
1774 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001775
reed@google.comea033602012-12-14 13:13:55 +00001776 CHECK_LOCKCOUNT_BALANCE(bitmap);
1777
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001778 SkRect storage;
1779 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001780 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001781 if (paint) {
1782 bounds = &paint->computeFastBounds(dst, &storage);
1783 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001784 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001785 return;
1786 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 }
reed@google.com3d608122011-11-21 15:16:16 +00001788
reed@google.com33535f32012-09-25 15:37:50 +00001789 SkLazyPaint lazy;
1790 if (NULL == paint) {
1791 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001793
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001794 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001795
reed@google.com33535f32012-09-25 15:37:50 +00001796 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001797 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001798 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001799
reed@google.com33535f32012-09-25 15:37:50 +00001800 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801}
1802
reed@google.com71121732012-09-18 15:14:33 +00001803void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001804 const SkRect& dst, const SkPaint* paint,
1805 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001806 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001807 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001808}
1809
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1811 const SkPaint* paint) {
1812 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001813 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814}
1815
reed@google.com9987ec32011-09-07 11:57:52 +00001816void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1817 const SkIRect& center, const SkRect& dst,
1818 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001819 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001820 SkRect storage;
1821 const SkRect* bounds = &dst;
1822 if (paint) {
1823 bounds = &paint->computeFastBounds(dst, &storage);
1824 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001825 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001826 return;
1827 }
1828 }
1829
reed@google.com9987ec32011-09-07 11:57:52 +00001830 const int32_t w = bitmap.width();
1831 const int32_t h = bitmap.height();
1832
1833 SkIRect c = center;
1834 // pin center to the bounds of the bitmap
1835 c.fLeft = SkMax32(0, center.fLeft);
1836 c.fTop = SkMax32(0, center.fTop);
1837 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1838 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1839
reed@google.com71121732012-09-18 15:14:33 +00001840 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001841 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001842 };
1843 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001844 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001845 };
reed@google.com9987ec32011-09-07 11:57:52 +00001846 SkScalar dstX[4] = {
1847 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1848 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1849 };
1850 SkScalar dstY[4] = {
1851 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1852 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1853 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001854
reed@google.com9987ec32011-09-07 11:57:52 +00001855 if (dstX[1] > dstX[2]) {
1856 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1857 dstX[2] = dstX[1];
1858 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001859
reed@google.com9987ec32011-09-07 11:57:52 +00001860 if (dstY[1] > dstY[2]) {
1861 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1862 dstY[2] = dstY[1];
1863 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001864
reed@google.com9987ec32011-09-07 11:57:52 +00001865 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001866 SkRect s, d;
1867
reed@google.com9987ec32011-09-07 11:57:52 +00001868 s.fTop = srcY[y];
1869 s.fBottom = srcY[y+1];
1870 d.fTop = dstY[y];
1871 d.fBottom = dstY[y+1];
1872 for (int x = 0; x < 3; x++) {
1873 s.fLeft = srcX[x];
1874 s.fRight = srcX[x+1];
1875 d.fLeft = dstX[x];
1876 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001877 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001878 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001879 }
1880 }
1881}
1882
1883void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1884 const SkRect& dst, const SkPaint* paint) {
1885 SkDEBUGCODE(bitmap.validate();)
1886
1887 // Need a device entry-point, so gpu can use a mesh
1888 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1889}
1890
reed@google.comf67e4cf2011-03-15 20:56:58 +00001891class SkDeviceFilteredPaint {
1892public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001893 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1894 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001895 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001896 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001897 newPaint->setFlags(flags.fFlags);
1898 newPaint->setHinting(flags.fHinting);
1899 fPaint = newPaint;
1900 } else {
1901 fPaint = &paint;
1902 }
1903 }
1904
reed@google.comf67e4cf2011-03-15 20:56:58 +00001905 const SkPaint& paint() const { return *fPaint; }
1906
1907private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001908 const SkPaint* fPaint;
1909 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001910};
1911
bungeman@google.com52c748b2011-08-22 21:30:43 +00001912void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1913 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001914 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001915 draw.fDevice->drawRect(draw, r, paint);
1916 } else {
1917 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001918 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001919 draw.fDevice->drawRect(draw, r, p);
1920 }
1921}
1922
1923void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1924 const char text[], size_t byteLength,
1925 SkScalar x, SkScalar y) {
1926 SkASSERT(byteLength == 0 || text != NULL);
1927
1928 // nothing to draw
1929 if (text == NULL || byteLength == 0 ||
1930 draw.fClip->isEmpty() ||
1931 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1932 return;
1933 }
1934
1935 SkScalar width = 0;
1936 SkPoint start;
1937
1938 start.set(0, 0); // to avoid warning
1939 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1940 SkPaint::kStrikeThruText_Flag)) {
1941 width = paint.measureText(text, byteLength);
1942
1943 SkScalar offsetX = 0;
1944 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1945 offsetX = SkScalarHalf(width);
1946 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1947 offsetX = width;
1948 }
1949 start.set(x - offsetX, y);
1950 }
1951
1952 if (0 == width) {
1953 return;
1954 }
1955
1956 uint32_t flags = paint.getFlags();
1957
1958 if (flags & (SkPaint::kUnderlineText_Flag |
1959 SkPaint::kStrikeThruText_Flag)) {
1960 SkScalar textSize = paint.getTextSize();
1961 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1962 SkRect r;
1963
1964 r.fLeft = start.fX;
1965 r.fRight = start.fX + width;
1966
1967 if (flags & SkPaint::kUnderlineText_Flag) {
1968 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1969 start.fY);
1970 r.fTop = offset;
1971 r.fBottom = offset + height;
1972 DrawRect(draw, paint, r, textSize);
1973 }
1974 if (flags & SkPaint::kStrikeThruText_Flag) {
1975 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1976 start.fY);
1977 r.fTop = offset;
1978 r.fBottom = offset + height;
1979 DrawRect(draw, paint, r, textSize);
1980 }
1981 }
1982}
1983
reed@android.com8a1c16f2008-12-17 15:59:43 +00001984void SkCanvas::drawText(const void* text, size_t byteLength,
1985 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001986 CHECK_SHADER_NOSETCONTEXT(paint);
1987
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001988 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001989
1990 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001991 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001992 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001993 DrawTextDecorations(iter, dfp.paint(),
1994 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995 }
1996
reed@google.com4e2b3d32011-04-07 14:18:59 +00001997 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001998}
1999
2000void SkCanvas::drawPosText(const void* text, size_t byteLength,
2001 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002002 CHECK_SHADER_NOSETCONTEXT(paint);
2003
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002004 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002005
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002007 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002009 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002010 }
reed@google.com4b226022011-01-11 18:32:13 +00002011
reed@google.com4e2b3d32011-04-07 14:18:59 +00002012 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002013}
2014
2015void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2016 const SkScalar xpos[], SkScalar constY,
2017 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002018 CHECK_SHADER_NOSETCONTEXT(paint);
2019
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002020 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002021
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002023 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002025 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 }
reed@google.com4b226022011-01-11 18:32:13 +00002027
reed@google.com4e2b3d32011-04-07 14:18:59 +00002028 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002029}
2030
2031void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2032 const SkPath& path, const SkMatrix* matrix,
2033 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002034 CHECK_SHADER_NOSETCONTEXT(paint);
2035
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002036 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037
2038 while (iter.next()) {
2039 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002040 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002041 }
2042
reed@google.com4e2b3d32011-04-07 14:18:59 +00002043 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044}
2045
2046void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2047 const SkPoint verts[], const SkPoint texs[],
2048 const SkColor colors[], SkXfermode* xmode,
2049 const uint16_t indices[], int indexCount,
2050 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002051 CHECK_SHADER_NOSETCONTEXT(paint);
2052
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002053 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002054
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055 while (iter.next()) {
2056 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002057 colors, xmode, indices, indexCount,
2058 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059 }
reed@google.com4b226022011-01-11 18:32:13 +00002060
reed@google.com4e2b3d32011-04-07 14:18:59 +00002061 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062}
2063
2064//////////////////////////////////////////////////////////////////////////////
2065// These methods are NOT virtual, and therefore must call back into virtual
2066// methods, rather than actually drawing themselves.
2067//////////////////////////////////////////////////////////////////////////////
2068
2069void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002070 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071 SkPaint paint;
2072
2073 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002074 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002075 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076 }
2077 this->drawPaint(paint);
2078}
2079
reed@android.com845fdac2009-06-23 03:01:32 +00002080void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 SkPaint paint;
2082
2083 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002084 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002085 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 }
2087 this->drawPaint(paint);
2088}
2089
2090void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2091 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002092
reed@android.com8a1c16f2008-12-17 15:59:43 +00002093 pt.set(x, y);
2094 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2095}
2096
2097void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2098 SkPoint pt;
2099 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002100
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 pt.set(x, y);
2102 paint.setColor(color);
2103 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2104}
2105
2106void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2107 const SkPaint& paint) {
2108 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002109
reed@android.com8a1c16f2008-12-17 15:59:43 +00002110 pts[0].set(x0, y0);
2111 pts[1].set(x1, y1);
2112 this->drawPoints(kLines_PointMode, 2, pts, paint);
2113}
2114
2115void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2116 SkScalar right, SkScalar bottom,
2117 const SkPaint& paint) {
2118 SkRect r;
2119
2120 r.set(left, top, right, bottom);
2121 this->drawRect(r, paint);
2122}
2123
2124void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2125 const SkPaint& paint) {
2126 if (radius < 0) {
2127 radius = 0;
2128 }
2129
2130 SkRect r;
2131 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002132 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133}
2134
2135void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2136 const SkPaint& paint) {
2137 if (rx > 0 && ry > 0) {
2138 if (paint.canComputeFastBounds()) {
2139 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002140 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141 return;
2142 }
2143 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002144 SkRRect rrect;
2145 rrect.setRectXY(r, rx, ry);
2146 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 } else {
2148 this->drawRect(r, paint);
2149 }
2150}
2151
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2153 SkScalar sweepAngle, bool useCenter,
2154 const SkPaint& paint) {
2155 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2156 this->drawOval(oval, paint);
2157 } else {
2158 SkPath path;
2159 if (useCenter) {
2160 path.moveTo(oval.centerX(), oval.centerY());
2161 }
2162 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2163 if (useCenter) {
2164 path.close();
2165 }
2166 this->drawPath(path, paint);
2167 }
2168}
2169
2170void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2171 const SkPath& path, SkScalar hOffset,
2172 SkScalar vOffset, const SkPaint& paint) {
2173 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002174
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175 matrix.setTranslate(hOffset, vOffset);
2176 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2177}
2178
reed@android.comf76bacf2009-05-13 14:00:33 +00002179///////////////////////////////////////////////////////////////////////////////
2180
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183}
2184
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185///////////////////////////////////////////////////////////////////////////////
2186///////////////////////////////////////////////////////////////////////////////
2187
2188SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002189 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190
2191 SkASSERT(canvas);
2192
2193 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2194 fDone = !fImpl->next();
2195}
2196
2197SkCanvas::LayerIter::~LayerIter() {
2198 fImpl->~SkDrawIter();
2199}
2200
2201void SkCanvas::LayerIter::next() {
2202 fDone = !fImpl->next();
2203}
2204
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002205SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 return fImpl->getDevice();
2207}
2208
2209const SkMatrix& SkCanvas::LayerIter::matrix() const {
2210 return fImpl->getMatrix();
2211}
2212
2213const SkPaint& SkCanvas::LayerIter::paint() const {
2214 const SkPaint* paint = fImpl->getPaint();
2215 if (NULL == paint) {
2216 paint = &fDefaultPaint;
2217 }
2218 return *paint;
2219}
2220
2221const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2222int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2223int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002224
2225///////////////////////////////////////////////////////////////////////////////
2226
2227SkCanvas::ClipVisitor::~ClipVisitor() { }