blob: 6c0fc884e1a53472f3a3681df8537c69dc283c6a [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"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
reed@google.comea033602012-12-14 13:13:55 +000094#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000095
96#else
97 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000098#endif
99
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000100typedef SkTLazy<SkPaint> SkLazyPaint;
101
reed@google.com97af1a62012-08-28 12:19:02 +0000102void SkCanvas::predrawNotify() {
103 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000104 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000105 }
106}
107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000110/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 The clip/matrix/proc are fields that reflect the top of the save/restore
112 stack. Whenever the canvas changes, it marks a dirty flag, and then before
113 these are used (assuming we're not on a layer) we rebuild these cache
114 values: they reflect the top of the save stack, but translated and clipped
115 by the device's XY offset and bitmap-bounds.
116*/
117struct DeviceCM {
118 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000119 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000120 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000122 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000124 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 : fNext(NULL) {
126 if (NULL != device) {
127 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000128 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 }
reed@google.com4b226022011-01-11 18:32:13 +0000130 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000132 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000134 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000136 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 fDevice->unref();
138 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000139 SkDELETE(fPaint);
140 }
reed@google.com4b226022011-01-11 18:32:13 +0000141
reed@google.com045e62d2011-10-24 12:19:46 +0000142 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
143 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 int x = fDevice->getOrigin().x();
145 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 int width = fDevice->width();
147 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000148
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 if ((x | y) == 0) {
150 fMatrix = &totalMatrix;
151 fClip = totalClip;
152 } else {
153 fMatrixStorage = totalMatrix;
154 fMatrixStorage.postTranslate(SkIntToScalar(-x),
155 SkIntToScalar(-y));
156 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 totalClip.translate(-x, -y, &fClip);
159 }
160
reed@google.com045e62d2011-10-24 12:19:46 +0000161 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162
163 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000164
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000166 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 SkRegion::kDifference_Op);
168 }
reed@google.com4b226022011-01-11 18:32:13 +0000169
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000170 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
171
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172#ifdef SK_DEBUG
173 if (!fClip.isEmpty()) {
174 SkIRect deviceR;
175 deviceR.set(0, 0, width, height);
176 SkASSERT(deviceR.contains(fClip.getBounds()));
177 }
178#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000179 }
180
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000182 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183};
184
185/* This is the record we keep for each save/restore level in the stack.
186 Since a level optionally copies the matrix and/or stack, we have pointers
187 for these fields. If the value is copied for this level, the copy is
188 stored in the ...Storage field, and the pointer points to that. If the
189 value is not copied for this level, we ignore ...Storage, and just point
190 at the corresponding value in the previous level in the stack.
191*/
192class SkCanvas::MCRec {
193public:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000194 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000195 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
196 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
197 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 DeviceCM* fLayer;
200 /* If there are any layers in the stack, this points to the top-most
201 one that is at or below this level in the stack (so we know what
202 bitmap/device to draw into from this level. This value is NOT
203 reference counted, since the real owner is either our fLayer field,
204 or a previous one in a lower level.)
205 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000206 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000208 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 if (NULL != prev) {
210 if (flags & SkCanvas::kMatrix_SaveFlag) {
211 fMatrixStorage = *prev->fMatrix;
212 fMatrix = &fMatrixStorage;
213 } else {
214 fMatrix = prev->fMatrix;
215 }
reed@google.com4b226022011-01-11 18:32:13 +0000216
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000218 fRasterClipStorage = *prev->fRasterClip;
219 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 } else {
reed@google.com00177082011-10-12 14:34:30 +0000221 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 }
223
224 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000225 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226
227 fTopLayer = prev->fTopLayer;
228 } else { // no prev
229 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000230
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000232 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 fFilter = NULL;
234 fTopLayer = NULL;
235 }
236 fLayer = NULL;
237
238 // don't bother initializing fNext
239 inc_rec();
240 }
241 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000242 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 SkDELETE(fLayer);
244 dec_rec();
245 }
reed@google.com4b226022011-01-11 18:32:13 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247private:
reed@google.com00177082011-10-12 14:34:30 +0000248 SkMatrix fMatrixStorage;
249 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250};
251
252class SkDrawIter : public SkDraw {
253public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000254 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000255 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000256 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 canvas->updateDeviceCMCache();
258
reed@google.com90c07ea2012-04-13 13:50:27 +0000259 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 fBounder = canvas->getBounder();
261 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000262 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 }
reed@google.com4b226022011-01-11 18:32:13 +0000264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 bool next() {
266 // skip over recs with empty clips
267 if (fSkipEmptyClips) {
268 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
269 fCurrLayer = fCurrLayer->fNext;
270 }
271 }
272
reed@google.comf68c5e22012-02-24 16:38:58 +0000273 const DeviceCM* rec = fCurrLayer;
274 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275
276 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000277 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
278 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 fDevice = rec->fDevice;
280 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000282 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283
284 fCurrLayer = rec->fNext;
285 if (fBounder) {
286 fBounder->setClip(fClip);
287 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 return true;
291 }
292 return false;
293 }
reed@google.com4b226022011-01-11 18:32:13 +0000294
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000295 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000296 int getX() const { return fDevice->getOrigin().x(); }
297 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 const SkMatrix& getMatrix() const { return *fMatrix; }
299 const SkRegion& getClip() const { return *fClip; }
300 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302private:
303 SkCanvas* fCanvas;
304 const DeviceCM* fCurrLayer;
305 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 SkBool8 fSkipEmptyClips;
307
308 typedef SkDraw INHERITED;
309};
310
311/////////////////////////////////////////////////////////////////////////////
312
313class AutoDrawLooper {
314public:
reed@google.com8926b162012-03-23 15:36:36 +0000315 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000316 bool skipLayerForImageFilter = false,
317 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000320 fPaint = NULL;
321 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000322 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000323 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324
reed@google.com8926b162012-03-23 15:36:36 +0000325 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
326 SkPaint tmp;
327 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000328 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
329 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000330 // we'll clear the imageFilter for the actual draws in next(), so
331 // it will only be applied during the restore().
332 fDoClearImageFilter = true;
333 }
334
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000335 if (SkDrawLooper* looper = paint.getLooper()) {
336 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
337 looper->contextSize());
338 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000339 fIsSimple = false;
340 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000341 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000342 // can we be marked as simple?
343 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000344 }
345 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000346
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000348 if (fDoClearImageFilter) {
349 fCanvas->internalRestore();
350 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000351 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000353
reed@google.com4e2b3d32011-04-07 14:18:59 +0000354 const SkPaint& paint() const {
355 SkASSERT(fPaint);
356 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000358
reed@google.com129ec222012-05-15 13:24:09 +0000359 bool next(SkDrawFilter::Type drawType) {
360 if (fDone) {
361 return false;
362 } else if (fIsSimple) {
363 fDone = true;
364 fPaint = &fOrigPaint;
365 return !fPaint->nothingToDraw();
366 } else {
367 return this->doNext(drawType);
368 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000369 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000370
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000372 SkLazyPaint fLazyPaint;
373 SkCanvas* fCanvas;
374 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000375 SkDrawFilter* fFilter;
376 const SkPaint* fPaint;
377 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000378 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000379 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000380 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000381 SkDrawLooper::Context* fLooperContext;
382 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000383
384 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385};
386
reed@google.com129ec222012-05-15 13:24:09 +0000387bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000388 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000389 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000390 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000391
392 SkPaint* paint = fLazyPaint.set(fOrigPaint);
393
394 if (fDoClearImageFilter) {
395 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000396 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000397
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000398 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000399 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000400 return false;
401 }
402 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000403 if (!fFilter->filter(paint, drawType)) {
404 fDone = true;
405 return false;
406 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000407 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000408 // no looper means we only draw once
409 fDone = true;
410 }
411 }
412 fPaint = paint;
413
414 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000415 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000416 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000417 }
418
419 // call this after any possible paint modifiers
420 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421 fPaint = NULL;
422 return false;
423 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000424 return true;
425}
426
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427/* Stack helper for managing a SkBounder. In the destructor, if we were
428 given a bounder, we call its commit() method, signifying that we are
429 done accumulating bounds for that draw.
430*/
431class SkAutoBounderCommit {
432public:
433 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
434 ~SkAutoBounderCommit() {
435 if (NULL != fBounder) {
436 fBounder->commit();
437 }
438 }
439private:
440 SkBounder* fBounder;
441};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000442#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443
444#include "SkColorPriv.h"
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446////////// macros to place around the internal draw calls //////////////////
447
reed@google.com8926b162012-03-23 15:36:36 +0000448#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000449 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000450 AutoDrawLooper looper(this, paint, true); \
451 while (looper.next(type)) { \
452 SkAutoBounderCommit ac(fBounder); \
453 SkDrawIter iter(this);
454
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000455#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000456 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000457 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 SkAutoBounderCommit ac(fBounder); \
460 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000461
reed@google.com4e2b3d32011-04-07 14:18:59 +0000462#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463
464////////////////////////////////////////////////////////////////////////////
465
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000466SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000468 fCachedLocalClipBounds.setEmpty();
469 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000470 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000471 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000472 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000473 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000474 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000475 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476
477 fMCRec = (MCRec*)fMCStack.push_back();
478 new (fMCRec) MCRec(NULL, 0);
479
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000480 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482
reed@google.com97af1a62012-08-28 12:19:02 +0000483 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000484
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000485 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486}
487
reed@google.comcde92112011-07-06 20:00:52 +0000488SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000489 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
490{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000491 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000492
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000493 this->init(NULL);
494}
495
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000496SkCanvas::SkCanvas(int width, int height)
497 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
498{
499 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000500
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000501 SkBitmap bitmap;
reed@google.com900ecf22014-02-20 20:55:37 +0000502 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000503 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
504}
505
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000506SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000507 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
508{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 inc_canvas();
510
511 this->init(device);
512}
513
514SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000515 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
516{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 inc_canvas();
518
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000519 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520}
521
522SkCanvas::~SkCanvas() {
523 // free up the contents of our deque
524 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000525 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000526
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 this->internalRestore(); // restore the last, since we're going away
528
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000529 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000530 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 dec_canvas();
533}
534
535SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
536 SkRefCnt_SafeAssign(fBounder, bounder);
537 return bounder;
538}
539
540SkDrawFilter* SkCanvas::getDrawFilter() const {
541 return fMCRec->fFilter;
542}
543
544SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
545 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
546 return filter;
547}
548
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000549SkMetaData& SkCanvas::getMetaData() {
550 // metadata users are rare, so we lazily allocate it. If that changes we
551 // can decide to just make it a field in the device (rather than a ptr)
552 if (NULL == fMetaData) {
553 fMetaData = new SkMetaData;
554 }
555 return *fMetaData;
556}
557
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558///////////////////////////////////////////////////////////////////////////////
559
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000560void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000561 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000562 if (device) {
563 device->flush();
564 }
565}
566
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000567SkISize SkCanvas::getTopLayerSize() const {
568 SkBaseDevice* d = this->getTopDevice();
569 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
570}
571
572SkIPoint SkCanvas::getTopLayerOrigin() const {
573 SkBaseDevice* d = this->getTopDevice();
574 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
575}
576
577SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000578 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000579 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
580}
581
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000582SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000584 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 SkASSERT(rec && rec->fLayer);
586 return rec->fLayer->fDevice;
587}
588
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000589SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000590 if (updateMatrixClip) {
591 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
592 }
reed@google.com9266fed2011-03-30 00:18:03 +0000593 return fMCRec->fTopLayer->fDevice;
594}
595
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000596SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000598 SkDeque::F2BIter iter(fMCStack);
599 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000601 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602
603 if (rootDevice == device) {
604 return device;
605 }
reed@google.com4b226022011-01-11 18:32:13 +0000606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000608 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 }
610 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000611 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 }
613
614 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
615 rootDevice = device;
616
617 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000618
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 /* Now we update our initial region to have the bounds of the new device,
620 and then intersect all of the clips in our stack with these bounds,
621 to ensure that we can't draw outside of the device's bounds (and trash
622 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000623
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 NOTE: this is only a partial-fix, since if the new device is larger than
625 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000626 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
628 reconstruct the correct clips, so this approximation will have to do.
629 The caller really needs to restore() back to the base if they want to
630 accurately take advantage of the new device bounds.
631 */
632
reed@google.com42aea282012-03-28 16:19:15 +0000633 SkIRect bounds;
634 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000636 } else {
637 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 }
reed@google.com42aea282012-03-28 16:19:15 +0000639 // now jam our 1st clip to be bounds, and intersect the rest with that
640 rec->fRasterClip->setRect(bounds);
641 while ((rec = (MCRec*)iter.next()) != NULL) {
642 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
643 }
644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 return device;
646}
647
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000648bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
649 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
650 return false;
651 }
652
653 bool weAllocated = false;
654 if (NULL == bitmap->pixelRef()) {
655 if (!bitmap->allocPixels()) {
656 return false;
657 }
658 weAllocated = true;
659 }
660
661 SkBitmap bm(*bitmap);
662 bm.lockPixels();
663 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
664 return true;
665 }
666
667 if (weAllocated) {
668 bitmap->setPixelRef(NULL);
669 }
670 return false;
671}
reed@google.com51df9e32010-12-23 19:29:18 +0000672
bsalomon@google.comc6980972011-11-02 19:57:21 +0000673bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000674 SkIRect r = srcRect;
675 const SkISize size = this->getBaseLayerSize();
676 if (!r.intersect(0, 0, size.width(), size.height())) {
677 bitmap->reset();
678 return false;
679 }
680
681 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
682 // bitmap will already be reset.
683 return false;
684 }
685 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
686 bitmap->reset();
687 return false;
688 }
689 return true;
690}
691
692bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
693 switch (origInfo.colorType()) {
694 case kUnknown_SkColorType:
695 case kIndex_8_SkColorType:
696 return false;
697 default:
698 break;
699 }
700 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
701 return false;
702 }
703 if (0 == origInfo.width() || 0 == origInfo.height()) {
704 return false;
705 }
706
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000707 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000708 if (!device) {
709 return false;
710 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000711
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000712 const SkISize size = this->getBaseLayerSize();
713 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
714 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000715 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000716 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000717
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000718 SkImageInfo info = origInfo;
719 // the intersect may have shrunk info's logical size
720 info.fWidth = srcR.width();
721 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000722
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000723 // if x or y are negative, then we have to adjust pixels
724 if (x > 0) {
725 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000726 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000727 if (y > 0) {
728 y = 0;
729 }
730 // here x,y are either 0 or negative
731 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000732
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000733 // The device can assert that the requested area is always contained in its bounds
734 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000735}
736
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000737bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
738 if (bitmap.getTexture()) {
739 return false;
740 }
741 SkBitmap bm(bitmap);
742 bm.lockPixels();
743 if (bm.getPixels()) {
744 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
745 }
746 return false;
747}
748
749bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
750 int x, int y) {
751 switch (origInfo.colorType()) {
752 case kUnknown_SkColorType:
753 case kIndex_8_SkColorType:
754 return false;
755 default:
756 break;
757 }
758 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
759 return false;
760 }
761
762 const SkISize size = this->getBaseLayerSize();
763 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
764 if (!target.intersect(0, 0, size.width(), size.height())) {
765 return false;
766 }
767
768 SkBaseDevice* device = this->getDevice();
769 if (!device) {
770 return false;
771 }
772
773 SkImageInfo info = origInfo;
774 // the intersect may have shrunk info's logical size
775 info.fWidth = target.width();
776 info.fHeight = target.height();
777
778 // if x or y are negative, then we have to adjust pixels
779 if (x > 0) {
780 x = 0;
781 }
782 if (y > 0) {
783 y = 0;
784 }
785 // here x,y are either 0 or negative
786 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
787
788 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000789 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000790}
reed@google.com51df9e32010-12-23 19:29:18 +0000791
junov@google.com4370aed2012-01-18 16:21:08 +0000792SkCanvas* SkCanvas::canvasForDrawIter() {
793 return this;
794}
795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796//////////////////////////////////////////////////////////////////////////////
797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798void SkCanvas::updateDeviceCMCache() {
799 if (fDeviceCMDirty) {
800 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000801 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000803
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000805 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000807 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000809 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 } while ((layer = layer->fNext) != NULL);
811 }
812 fDeviceCMDirty = false;
813 }
814}
815
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816///////////////////////////////////////////////////////////////////////////////
817
818int SkCanvas::internalSave(SaveFlags flags) {
819 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000820
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 MCRec* newTop = (MCRec*)fMCStack.push_back();
822 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000823
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000825
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000826 if (SkCanvas::kClip_SaveFlag & flags) {
827 fClipStack.save();
828 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000829
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 return saveCount;
831}
832
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000833void SkCanvas::willSave(SaveFlags) {
834 // Do nothing. Subclasses may do something.
835}
836
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000837int SkCanvas::save() {
838 this->willSave(kMatrixClip_SaveFlag);
839 return this->internalSave(kMatrixClip_SaveFlag);
840}
841
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000843 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 // call shared impl
845 return this->internalSave(flags);
846}
847
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000849#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000851#else
852 return true;
853#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854}
855
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000857 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000858 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000859 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000860 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000861 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000862 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000863
864 if (imageFilter) {
865 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
866 // Filters may grow the bounds beyond the device bounds.
867 op = SkRegion::kReplace_Op;
868 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000869 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 if (NULL != bounds) {
871 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000872
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 this->getTotalMatrix().mapRect(&r, *bounds);
874 r.roundOut(&ir);
875 // early exit if the layer's bounds are clipped out
876 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000877 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000878 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000879 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000880 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 }
882 } else { // no user bounds, so just use the clip
883 ir = clipBounds;
884 }
885
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000886 if (bounds_affects_clip(flags)) {
887 fClipStack.clipDevRect(ir, op);
888 // early exit if the clip is now empty
889 if (!fMCRec->fRasterClip->op(ir, op)) {
890 return false;
891 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000892 }
893
894 if (intersection) {
895 *intersection = ir;
896 }
897 return true;
898}
899
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000900SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) {
901
902 // Do nothing. Subclasses may do something.
903 return kFullLayer_SaveLayerStrategy;
904}
905
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000906int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
907 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
908 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
909}
910
junov@chromium.orga907ac32012-02-24 21:54:07 +0000911int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
912 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000913 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
914 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000915}
916
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000917static SkBaseDevice* create_compatible_device(SkCanvas* canvas,
918 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000919 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000920 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000921}
922
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000923int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
924 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000925#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
926 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
927#endif
928
junov@chromium.orga907ac32012-02-24 21:54:07 +0000929 // do this before we create the layer. We don't call the public save() since
930 // that would invoke a possibly overridden virtual
931 int count = this->internalSave(flags);
932
933 fDeviceCMDirty = true;
934
935 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000936 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 return count;
938 }
939
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000940 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
941 // the clipRectBounds() call above?
942 if (kNoLayer_SaveLayerStrategy == strategy) {
943 return count;
944 }
945
reed@google.comb55deeb2012-01-06 14:43:09 +0000946 // Kill the imagefilter if our device doesn't allow it
947 SkLazyPaint lazyP;
948 if (paint && paint->getImageFilter()) {
949 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000950 if (justForImageFilter) {
951 // early exit if the layer was just for the imageFilter
952 return count;
953 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000954 SkPaint* p = lazyP.set(*paint);
955 p->setImageFilter(NULL);
956 paint = p;
957 }
958 }
959
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000960 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
961 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
962 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000964 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000965 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000966 device = create_compatible_device(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000967 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000968 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000969 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000970 if (NULL == device) {
971 SkDebugf("Unable to create device for layer.");
972 return count;
973 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000974
reed@google.com6f8f2922011-03-04 22:27:10 +0000975 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000976 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 device->unref();
978
979 layer->fNext = fMCRec->fTopLayer;
980 fMCRec->fLayer = layer;
981 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
982
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000983 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 return count;
985}
986
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000987int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
988 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
989}
990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
992 SaveFlags flags) {
993 if (0xFF == alpha) {
994 return this->saveLayer(bounds, NULL, flags);
995 } else {
996 SkPaint tmpPaint;
997 tmpPaint.setAlpha(alpha);
998 return this->saveLayer(bounds, &tmpPaint, flags);
999 }
1000}
1001
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001002void SkCanvas::willRestore() {
1003 // Do nothing. Subclasses may do something.
1004}
1005
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006void SkCanvas::restore() {
1007 // check for underflow
1008 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001009 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 this->internalRestore();
1011 }
1012}
1013
1014void SkCanvas::internalRestore() {
1015 SkASSERT(fMCStack.count() != 0);
1016
1017 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001018 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001020 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
1021 fClipStack.restore();
1022 }
1023
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001024 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 DeviceCM* layer = fMCRec->fLayer; // may be null
1026 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1027 fMCRec->fLayer = NULL;
1028
1029 // now do the normal restore()
1030 fMCRec->~MCRec(); // balanced in save()
1031 fMCStack.pop_back();
1032 fMCRec = (MCRec*)fMCStack.back();
1033
1034 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1035 since if we're being recorded, we don't want to record this (the
1036 recorder will have already recorded the restore).
1037 */
1038 if (NULL != layer) {
1039 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001040 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001041 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1042 layer->fPaint);
1043 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001045
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001046 SkASSERT(fSaveLayerCount > 0);
1047 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 }
1049 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001050 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051}
1052
1053int SkCanvas::getSaveCount() const {
1054 return fMCStack.count();
1055}
1056
1057void SkCanvas::restoreToCount(int count) {
1058 // sanity check
1059 if (count < 1) {
1060 count = 1;
1061 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001062
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001063 int n = this->getSaveCount() - count;
1064 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 this->restore();
1066 }
1067}
1068
reed@google.com7c202932011-12-14 18:48:05 +00001069bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001070 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001071}
1072
reed@google.com76f10a32014-02-05 15:32:21 +00001073SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1074 return this->onNewSurface(info);
1075}
1076
1077SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1078 SkBaseDevice* dev = this->getDevice();
1079 return dev ? dev->newSurface(info) : NULL;
1080}
1081
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001082SkImageInfo SkCanvas::imageInfo() const {
1083 SkBaseDevice* dev = this->getDevice();
1084 if (dev) {
1085 return dev->imageInfo();
1086 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001087 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001088 }
1089}
1090
1091const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1092 return this->onPeekPixels(info, rowBytes);
1093}
1094
1095const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1096 SkBaseDevice* dev = this->getDevice();
1097 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1098}
1099
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001100void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1101 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1102 if (pixels && origin) {
1103 *origin = this->getTopDevice(false)->getOrigin();
1104 }
1105 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001106}
1107
1108void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1109 SkBaseDevice* dev = this->getTopDevice();
1110 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1111}
1112
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001113SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1114 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1115 if (NULL == fAddr) {
1116 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001117 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001118 return; // failure, fAddr is NULL
1119 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001120 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1121 return; // failure, fAddr is NULL
1122 }
1123 fAddr = fBitmap.getPixels();
1124 fRowBytes = fBitmap.rowBytes();
1125 }
1126 SkASSERT(fAddr); // success
1127}
1128
1129bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1130 if (fAddr) {
1131 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1132 NULL, NULL);
1133 } else {
1134 bitmap->reset();
1135 return false;
1136 }
1137}
1138
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001139void SkCanvas::onPushCull(const SkRect& cullRect) {
1140 // do nothing. Subclasses may do something
1141}
1142
1143void SkCanvas::onPopCull() {
1144 // do nothing. Subclasses may do something
1145}
1146
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001148#ifdef SK_DEBUG
1149// Ensure that cull rects are monotonically nested in device space.
1150void SkCanvas::validateCull(const SkIRect& devCull) {
1151 if (fCullStack.isEmpty()
1152 || devCull.isEmpty()
1153 || fCullStack.top().contains(devCull)) {
1154 return;
1155 }
1156
1157 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1158 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1159 fCullStack.top().x(), fCullStack.top().y(),
1160 fCullStack.top().right(), fCullStack.top().bottom()));
1161
1162#ifdef ASSERT_NESTED_CULLING
1163 SkDEBUGFAIL("Invalid cull.");
1164#endif
1165}
1166#endif
1167
1168void SkCanvas::pushCull(const SkRect& cullRect) {
1169 ++fCullCount;
1170 this->onPushCull(cullRect);
1171
1172#ifdef SK_DEBUG
1173 // Map the cull rect into device space.
1174 SkRect mappedCull;
1175 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1176
1177 // Take clipping into account.
1178 SkIRect devClip, devCull;
1179 mappedCull.roundOut(&devCull);
1180 this->getClipDeviceBounds(&devClip);
1181 if (!devCull.intersect(devClip)) {
1182 devCull.setEmpty();
1183 }
1184
1185 this->validateCull(devCull);
1186 fCullStack.push(devCull); // balanced in popCull
1187#endif
1188}
1189
1190void SkCanvas::popCull() {
1191 SkASSERT(fCullStack.count() == fCullCount);
1192
1193 if (fCullCount > 0) {
1194 --fCullCount;
1195 this->onPopCull();
1196
1197 SkDEBUGCODE(fCullStack.pop());
1198 }
1199}
1200
1201/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001203void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001205 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 return;
1207 }
1208
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001209 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001211 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001213
1214 SkDEBUGCODE(bitmap.validate();)
1215 CHECK_LOCKCOUNT_BALANCE(bitmap);
1216
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001217 SkRect storage;
1218 const SkRect* bounds = NULL;
1219 if (paint && paint->canComputeFastBounds()) {
1220 bitmap.getBounds(&storage);
1221 matrix.mapRect(&storage);
1222 bounds = &paint->computeFastBounds(storage, &storage);
1223 }
1224
1225 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001226
1227 while (iter.next()) {
1228 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1229 }
1230
1231 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232}
1233
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001234void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001235 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 SkPaint tmp;
1237 if (NULL == paint) {
1238 tmp.setDither(true);
1239 paint = &tmp;
1240 }
reed@google.com4b226022011-01-11 18:32:13 +00001241
reed@google.com8926b162012-03-23 15:36:36 +00001242 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001244 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001245 paint = &looper.paint();
1246 SkImageFilter* filter = paint->getImageFilter();
1247 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001248 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001249 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001250 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001251 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001252 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001253 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001254 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001255 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001256 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1257 SkAutoUnref aur(NULL);
1258 if (!cache) {
1259 cache = SkImageFilter::Cache::Create();
1260 aur.reset(cache);
1261 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001262 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001263 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001264 SkPaint tmpUnfiltered(*paint);
1265 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001266 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1267 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001268 }
1269 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001270 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001271 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001273 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274}
1275
reed@google.com8926b162012-03-23 15:36:36 +00001276void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1277 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001278 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001279 return;
1280 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001281 SkDEBUGCODE(bitmap.validate();)
1282 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001283
reed@google.com8926b162012-03-23 15:36:36 +00001284 SkPaint tmp;
1285 if (NULL == paint) {
1286 paint = &tmp;
1287 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001288
reed@google.com8926b162012-03-23 15:36:36 +00001289 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001290
reed@google.com8926b162012-03-23 15:36:36 +00001291 while (iter.next()) {
1292 paint = &looper.paint();
1293 SkImageFilter* filter = paint->getImageFilter();
1294 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1295 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001296 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001297 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001298 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001299 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001300 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001301 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001302 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1303 SkAutoUnref aur(NULL);
1304 if (!cache) {
1305 cache = SkImageFilter::Cache::Create();
1306 aur.reset(cache);
1307 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001308 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001309 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001310 SkPaint tmpUnfiltered(*paint);
1311 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001312 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001313 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001314 }
1315 } else {
1316 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1317 }
1318 }
1319 LOOPER_END
1320}
1321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001323void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001324 SkMatrix m;
1325 m.setTranslate(dx, dy);
1326 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001329void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001330 SkMatrix m;
1331 m.setScale(sx, sy);
1332 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333}
1334
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001335void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001336 SkMatrix m;
1337 m.setRotate(degrees);
1338 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339}
1340
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001341void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001342 SkMatrix m;
1343 m.setSkew(sx, sy);
1344 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001345}
1346
1347void SkCanvas::didConcat(const SkMatrix&) {
1348 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349}
1350
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001351void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001352 if (matrix.isIdentity()) {
1353 return;
1354 }
1355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001357 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001358 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001359
1360 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001361}
1362
1363void SkCanvas::didSetMatrix(const SkMatrix&) {
1364 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365}
1366
1367void SkCanvas::setMatrix(const SkMatrix& matrix) {
1368 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001369 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001371 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372}
1373
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374void SkCanvas::resetMatrix() {
1375 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001376
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 matrix.reset();
1378 this->setMatrix(matrix);
1379}
1380
1381//////////////////////////////////////////////////////////////////////////////
1382
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001383void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001384 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1385 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001386}
1387
1388void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001389#ifdef SK_ENABLE_CLIP_QUICKREJECT
1390 if (SkRegion::kIntersect_Op == op) {
1391 if (fMCRec->fRasterClip->isEmpty()) {
1392 return false;
1393 }
1394
reed@google.com3b3e8952012-08-16 20:53:31 +00001395 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001396 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001397 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001398
1399 fClipStack.clipEmpty();
1400 return fMCRec->fRasterClip->setEmpty();
1401 }
1402 }
1403#endif
1404
reed@google.com5c3d1472011-02-22 19:12:23 +00001405 AutoValidateClip avc(this);
1406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001408 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001409 if (!fAllowSoftClip) {
1410 edgeStyle = kHard_ClipEdgeStyle;
1411 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
1413 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001414 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001415 // the matrix. This means we don't have to a) make a path, and b) tell
1416 // the region code to scan-convert the path, only to discover that it
1417 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419
1420 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1422 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001424 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001425 // and clip against that, since it can handle any matrix. However, to
1426 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1427 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 SkPath path;
1429
1430 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001431 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 }
1433}
1434
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001435static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1436 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001437 // base is used to limit the size (and therefore memory allocation) of the
1438 // region that results from scan converting devPath.
1439 SkRegion base;
1440
reed@google.com819c9212011-02-23 18:56:55 +00001441 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001442 // since we are intersect, we can do better (tighter) with currRgn's
1443 // bounds, than just using the device. However, if currRgn is complex,
1444 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001445 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001446 // FIXME: we should also be able to do this when currClip->isBW(),
1447 // but relaxing the test above triggers GM asserts in
1448 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001449 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001450 } else {
reed@google.com00177082011-10-12 14:34:30 +00001451 base.setRect(currClip->getBounds());
1452 SkRasterClip clip;
1453 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001454 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001455 }
reed@google.com819c9212011-02-23 18:56:55 +00001456 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001457 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001458 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001459 currClip->setEmpty();
1460 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001461 }
1462
junov@chromium.orga907ac32012-02-24 21:54:07 +00001463 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001464
1465 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001466 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001467 } else {
reed@google.com00177082011-10-12 14:34:30 +00001468 SkRasterClip clip;
1469 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001470 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001471 }
1472 }
1473}
1474
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001475void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001476 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001477 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001478 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1479 } else {
1480 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001481 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001482}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001483
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001484void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001485 SkRRect transformedRRect;
1486 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1487 AutoValidateClip avc(this);
1488
1489 fDeviceCMDirty = true;
1490 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001491 if (!fAllowSoftClip) {
1492 edgeStyle = kHard_ClipEdgeStyle;
1493 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001494
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001495 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001496
1497 SkPath devPath;
1498 devPath.addRRect(transformedRRect);
1499
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001500 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1501 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001502 }
1503
1504 SkPath path;
1505 path.addRRect(rrect);
1506 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001507 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001508}
1509
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001510void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001511 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1512 SkRect r;
1513 if (!path.isInverseFillType() && path.isRect(&r)) {
1514 this->onClipRect(r, op, edgeStyle);
1515 } else {
1516 this->onClipPath(path, op, edgeStyle);
1517 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001518}
1519
1520void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001521#ifdef SK_ENABLE_CLIP_QUICKREJECT
1522 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1523 if (fMCRec->fRasterClip->isEmpty()) {
1524 return false;
1525 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001526
reed@google.com3b3e8952012-08-16 20:53:31 +00001527 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001528 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001529 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001530
reed@google.comda17f752012-08-16 18:27:05 +00001531 fClipStack.clipEmpty();
1532 return fMCRec->fRasterClip->setEmpty();
1533 }
1534 }
1535#endif
1536
reed@google.com5c3d1472011-02-22 19:12:23 +00001537 AutoValidateClip avc(this);
1538
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001540 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001541 if (!fAllowSoftClip) {
1542 edgeStyle = kHard_ClipEdgeStyle;
1543 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544
1545 SkPath devPath;
1546 path.transform(*fMCRec->fMatrix, &devPath);
1547
reed@google.comfe701122011-11-08 19:41:23 +00001548 // Check if the transfomation, or the original path itself
1549 // made us empty. Note this can also happen if we contained NaN
1550 // values. computing the bounds detects this, and will set our
1551 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1552 if (devPath.getBounds().isEmpty()) {
1553 // resetting the path will remove any NaN or other wanky values
1554 // that might upset our scan converter.
1555 devPath.reset();
1556 }
1557
reed@google.com5c3d1472011-02-22 19:12:23 +00001558 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001559 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001560
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001561 if (fAllowSimplifyClip) {
1562 devPath.reset();
1563 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1564 const SkClipStack* clipStack = getClipStack();
1565 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1566 const SkClipStack::Element* element;
1567 while ((element = iter.next())) {
1568 SkClipStack::Element::Type type = element->getType();
1569 if (type == SkClipStack::Element::kEmpty_Type) {
1570 continue;
1571 }
1572 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001573 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001574 SkRegion::Op elementOp = element->getOp();
1575 if (elementOp == SkRegion::kReplace_Op) {
1576 devPath = operand;
1577 } else {
1578 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1579 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001580 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1581 // perhaps we need an API change to avoid this sort of mixed-signals about
1582 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 if (element->isAA()) {
1584 edgeStyle = kSoft_ClipEdgeStyle;
1585 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001586 }
1587 op = SkRegion::kReplace_Op;
1588 }
1589
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001590 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591}
1592
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001593void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001594 bool inverseFilled) {
1595 // This is for updating the clip conservatively using only bounds
1596 // information.
1597 // Contract:
1598 // The current clip must contain the true clip. The true
1599 // clip is the clip that would have normally been computed
1600 // by calls to clipPath and clipRRect
1601 // Objective:
1602 // Keep the current clip as small as possible without
1603 // breaking the contract, using only clip bounding rectangles
1604 // (for performance).
1605
1606 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1607 // don't have to worry about getting caught in a loop. Thus anywhere
1608 // we call a virtual method, we explicitly prefix it with
1609 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001610
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001611 if (inverseFilled) {
1612 switch (op) {
1613 case SkRegion::kIntersect_Op:
1614 case SkRegion::kDifference_Op:
1615 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616 // the clip unchanged conservatively respects the contract.
1617 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001618 case SkRegion::kUnion_Op:
1619 case SkRegion::kReplace_Op:
1620 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001622 // These ops can grow the current clip up to the extents of
1623 // the input clip, which is inverse filled, so we just set
1624 // the current clip to the device bounds.
1625 SkRect deviceBounds;
1626 SkIRect deviceIBounds;
1627 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001628 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001629
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001630 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001631 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001632 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001633 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001635 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001636 break;
1637 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001638 default:
1639 SkASSERT(0); // unhandled op?
1640 }
1641 } else {
1642 // Not inverse filled
1643 switch (op) {
1644 case SkRegion::kIntersect_Op:
1645 case SkRegion::kUnion_Op:
1646 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001647 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1648 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001649 case SkRegion::kDifference_Op:
1650 // Difference can only shrink the current clip.
1651 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001652 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001653 case SkRegion::kReverseDifference_Op:
1654 // To reverse, we swap in the bounds with a replace op.
1655 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001656 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1657 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001658 case SkRegion::kXOR_Op:
1659 // Be conservative, based on (A XOR B) always included in (A union B),
1660 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001661 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1662 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001663 default:
1664 SkASSERT(0); // unhandled op?
1665 }
1666 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001667}
1668
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001669void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001670 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001671}
1672
1673void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001674 AutoValidateClip avc(this);
1675
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001677 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678
reed@google.com5c3d1472011-02-22 19:12:23 +00001679 // todo: signal fClipStack that we have a region, and therefore (I guess)
1680 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001681 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001682
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001683 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684}
1685
reed@google.com819c9212011-02-23 18:56:55 +00001686#ifdef SK_DEBUG
1687void SkCanvas::validateClip() const {
1688 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001689 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001690 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001691 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001692 return;
1693 }
1694
reed@google.com819c9212011-02-23 18:56:55 +00001695 SkIRect ir;
1696 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001697 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001698
robertphillips@google.com80214e22012-07-20 15:33:18 +00001699 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001700 const SkClipStack::Element* element;
1701 while ((element = iter.next()) != NULL) {
1702 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001703 case SkClipStack::Element::kRect_Type:
1704 element->getRect().round(&ir);
1705 tmpClip.op(ir, element->getOp());
1706 break;
1707 case SkClipStack::Element::kEmpty_Type:
1708 tmpClip.setEmpty();
1709 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001710 default: {
1711 SkPath path;
1712 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001713 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001714 break;
1715 }
reed@google.com819c9212011-02-23 18:56:55 +00001716 }
1717 }
reed@google.com819c9212011-02-23 18:56:55 +00001718}
1719#endif
1720
reed@google.com90c07ea2012-04-13 13:50:27 +00001721void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001722 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001723 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001724
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001725 static const SkRect kEmpty = { 0, 0, 0, 0 };
1726 while ((element = iter.next()) != NULL) {
1727 switch (element->getType()) {
1728 case SkClipStack::Element::kPath_Type:
1729 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1730 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001731 case SkClipStack::Element::kRRect_Type:
1732 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1733 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001734 case SkClipStack::Element::kRect_Type:
1735 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1736 break;
1737 case SkClipStack::Element::kEmpty_Type:
1738 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1739 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001740 }
1741 }
1742}
1743
reed@google.com5c3d1472011-02-22 19:12:23 +00001744///////////////////////////////////////////////////////////////////////////////
1745
reed@google.com754de5f2014-02-24 19:38:20 +00001746bool SkCanvas::isClipEmpty() const {
1747 return fMCRec->fRasterClip->isEmpty();
1748}
1749
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001750bool SkCanvas::isClipRect() const {
1751 return fMCRec->fRasterClip->isRect();
1752}
1753
reed@google.com3b3e8952012-08-16 20:53:31 +00001754bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001755
reed@google.com16078632011-12-06 18:56:37 +00001756 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001757 return true;
1758
reed@google.com00177082011-10-12 14:34:30 +00001759 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760 return true;
1761 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762
tomhudson@google.com8d430182011-06-06 19:11:19 +00001763 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001764 SkRect dst;
1765 fMCRec->fMatrix->mapRect(&dst, rect);
1766 SkIRect idst;
1767 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001768 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001769 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001770 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001771
reed@android.coma380ae42009-07-21 01:17:02 +00001772 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001773 // TODO: should we use | instead, or compare all 4 at once?
1774 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001775 return true;
1776 }
reed@google.comc0784db2013-12-13 21:16:12 +00001777 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001778 return true;
1779 }
1780 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782}
1783
reed@google.com3b3e8952012-08-16 20:53:31 +00001784bool SkCanvas::quickReject(const SkPath& path) const {
1785 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
reed@google.com3b3e8952012-08-16 20:53:31 +00001788bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001789 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001790 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791 return false;
1792 }
1793
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001794 SkMatrix inverse;
1795 // if we can't invert the CTM, we can't return local clip bounds
1796 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001797 if (bounds) {
1798 bounds->setEmpty();
1799 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001800 return false;
1801 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001803 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001804 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001805 // adjust it outwards in case we are antialiasing
1806 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001807
reed@google.com8f4d2302013-12-17 16:44:46 +00001808 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1809 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 inverse.mapRect(bounds, r);
1811 }
1812 return true;
1813}
1814
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001815bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001816 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001817 if (clip.isEmpty()) {
1818 if (bounds) {
1819 bounds->setEmpty();
1820 }
1821 return false;
1822 }
1823
1824 if (NULL != bounds) {
1825 *bounds = clip.getBounds();
1826 }
1827 return true;
1828}
1829
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830const SkMatrix& SkCanvas::getTotalMatrix() const {
1831 return *fMCRec->fMatrix;
1832}
1833
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001834#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001835SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001836 if (fMCRec->fRasterClip->isEmpty()) {
1837 return kEmpty_ClipType;
1838 }
1839 if (fMCRec->fRasterClip->isRect()) {
1840 return kRect_ClipType;
1841 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001842 return kComplex_ClipType;
1843}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001844#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001845
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001846#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001848 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001850#endif
1851
1852const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1853 return fMCRec->fRasterClip->forceGetBW();
1854}
1855
1856void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1857 path->reset();
1858
1859 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1860 if (rgn.isEmpty()) {
1861 return;
1862 }
1863 (void)rgn.getBoundaryPath(path);
1864}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865
reed@google.com9c135db2014-03-12 18:28:35 +00001866GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1867 SkBaseDevice* dev = this->getTopDevice();
1868 return dev ? dev->accessRenderTarget() : NULL;
1869}
1870
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001871SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001872 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001873 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874}
1875
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001876GrContext* SkCanvas::getGrContext() {
1877#if SK_SUPPORT_GPU
1878 SkBaseDevice* device = this->getTopDevice();
1879 if (NULL != device) {
1880 GrRenderTarget* renderTarget = device->accessRenderTarget();
1881 if (NULL != renderTarget) {
1882 return renderTarget->getContext();
1883 }
1884 }
1885#endif
1886
1887 return NULL;
1888
1889}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001890
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001891void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1892 const SkPaint& paint) {
1893 if (outer.isEmpty()) {
1894 return;
1895 }
1896 if (inner.isEmpty()) {
1897 this->drawRRect(outer, paint);
1898 return;
1899 }
1900
1901 // We don't have this method (yet), but technically this is what we should
1902 // be able to assert...
1903 // SkASSERT(outer.contains(inner));
1904 //
1905 // For now at least check for containment of bounds
1906 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1907
1908 this->onDrawDRRect(outer, inner, paint);
1909}
1910
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911//////////////////////////////////////////////////////////////////////////////
1912// These are the virtual drawing methods
1913//////////////////////////////////////////////////////////////////////////////
1914
reed@google.com2a981812011-04-14 18:59:28 +00001915void SkCanvas::clear(SkColor color) {
1916 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001917 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001918 while (iter.next()) {
1919 iter.fDevice->clear(color);
1920 }
1921}
1922
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001923void SkCanvas::onDiscard() {
1924 if (NULL != fSurfaceBase) {
1925 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1926 }
1927}
1928
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001930 this->internalDrawPaint(paint);
1931}
1932
1933void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001934 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001935
1936 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001937 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 }
1939
reed@google.com4e2b3d32011-04-07 14:18:59 +00001940 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941}
1942
1943void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1944 const SkPaint& paint) {
1945 if ((long)count <= 0) {
1946 return;
1947 }
1948
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001949 SkRect r, storage;
1950 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001951 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001952 // special-case 2 points (common for drawing a single line)
1953 if (2 == count) {
1954 r.set(pts[0], pts[1]);
1955 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001956 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001957 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001958 bounds = &paint.computeFastStrokeBounds(r, &storage);
1959 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001960 return;
1961 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001962 }
reed@google.coma584aed2012-05-16 14:06:02 +00001963
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964 SkASSERT(pts != NULL);
1965
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001966 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001967
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001969 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001970 }
reed@google.com4b226022011-01-11 18:32:13 +00001971
reed@google.com4e2b3d32011-04-07 14:18:59 +00001972 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973}
1974
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001975void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001976 SkRect storage;
1977 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001979 bounds = &paint.computeFastBounds(r, &storage);
1980 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001981 return;
1982 }
1983 }
reed@google.com4b226022011-01-11 18:32:13 +00001984
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001985 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986
1987 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001988 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001989 }
1990
reed@google.com4e2b3d32011-04-07 14:18:59 +00001991 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992}
1993
reed@google.com4ed0fb72012-12-12 20:48:18 +00001994void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001995 SkRect storage;
1996 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001997 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001998 bounds = &paint.computeFastBounds(oval, &storage);
1999 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002000 return;
2001 }
2002 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002003
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002004 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002005
2006 while (iter.next()) {
2007 iter.fDevice->drawOval(iter, oval, looper.paint());
2008 }
2009
2010 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002011}
2012
2013void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002014 SkRect storage;
2015 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002016 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002017 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2018 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002019 return;
2020 }
2021 }
2022
2023 if (rrect.isRect()) {
2024 // call the non-virtual version
2025 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002026 return;
2027 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002028 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002029 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2030 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002031 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002032
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002033 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002034
2035 while (iter.next()) {
2036 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2037 }
2038
2039 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002040}
2041
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002042void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2043 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002044 SkRect storage;
2045 const SkRect* bounds = NULL;
2046 if (paint.canComputeFastBounds()) {
2047 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2048 if (this->quickReject(*bounds)) {
2049 return;
2050 }
2051 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002052
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002053 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002054
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002055 while (iter.next()) {
2056 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2057 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002058
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002059 LOOPER_END
2060}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002061
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00002062void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002063 if (!path.isFinite()) {
2064 return;
2065 }
2066
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002067 SkRect storage;
2068 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002069 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002070 const SkRect& pathBounds = path.getBounds();
2071 bounds = &paint.computeFastBounds(pathBounds, &storage);
2072 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 return;
2074 }
2075 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002076
2077 const SkRect& r = path.getBounds();
2078 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002079 if (path.isInverseFillType()) {
2080 this->internalDrawPaint(paint);
2081 }
2082 return;
2083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002085 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086
2087 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002088 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 }
2090
reed@google.com4e2b3d32011-04-07 14:18:59 +00002091 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092}
2093
2094void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2095 const SkPaint* paint) {
2096 SkDEBUGCODE(bitmap.validate();)
2097
reed@google.com3d608122011-11-21 15:16:16 +00002098 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002099 SkRect bounds = {
2100 x, y,
2101 x + SkIntToScalar(bitmap.width()),
2102 y + SkIntToScalar(bitmap.height())
2103 };
2104 if (paint) {
2105 (void)paint->computeFastBounds(bounds, &bounds);
2106 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002107 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108 return;
2109 }
2110 }
reed@google.com4b226022011-01-11 18:32:13 +00002111
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 SkMatrix matrix;
2113 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002114 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002115}
2116
reed@google.com9987ec32011-09-07 11:57:52 +00002117// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002118void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002119 const SkRect& dst, const SkPaint* paint,
2120 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002121 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 return;
2123 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002124
reed@google.comea033602012-12-14 13:13:55 +00002125 CHECK_LOCKCOUNT_BALANCE(bitmap);
2126
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002127 SkRect storage;
2128 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002129 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002130 if (paint) {
2131 bounds = &paint->computeFastBounds(dst, &storage);
2132 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002133 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002134 return;
2135 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136 }
reed@google.com3d608122011-11-21 15:16:16 +00002137
reed@google.com33535f32012-09-25 15:37:50 +00002138 SkLazyPaint lazy;
2139 if (NULL == paint) {
2140 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002142
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002143 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002144
reed@google.com33535f32012-09-25 15:37:50 +00002145 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002146 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002147 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002148
reed@google.com33535f32012-09-25 15:37:50 +00002149 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150}
2151
reed@google.com71121732012-09-18 15:14:33 +00002152void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002153 const SkRect& dst, const SkPaint* paint,
2154 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002155 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002156 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002157}
2158
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2160 const SkPaint* paint) {
2161 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002162 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002163}
2164
reed@google.com9987ec32011-09-07 11:57:52 +00002165void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2166 const SkIRect& center, const SkRect& dst,
2167 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002168 if (bitmap.drawsNothing()) {
2169 return;
2170 }
reed@google.com3d608122011-11-21 15:16:16 +00002171 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002172 SkRect storage;
2173 const SkRect* bounds = &dst;
2174 if (paint) {
2175 bounds = &paint->computeFastBounds(dst, &storage);
2176 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002177 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002178 return;
2179 }
2180 }
2181
reed@google.com9987ec32011-09-07 11:57:52 +00002182 const int32_t w = bitmap.width();
2183 const int32_t h = bitmap.height();
2184
2185 SkIRect c = center;
2186 // pin center to the bounds of the bitmap
2187 c.fLeft = SkMax32(0, center.fLeft);
2188 c.fTop = SkMax32(0, center.fTop);
2189 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2190 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2191
reed@google.com71121732012-09-18 15:14:33 +00002192 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002193 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002194 };
2195 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002196 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002197 };
reed@google.com9987ec32011-09-07 11:57:52 +00002198 SkScalar dstX[4] = {
2199 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2200 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2201 };
2202 SkScalar dstY[4] = {
2203 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2204 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2205 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002206
reed@google.com9987ec32011-09-07 11:57:52 +00002207 if (dstX[1] > dstX[2]) {
2208 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2209 dstX[2] = dstX[1];
2210 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002211
reed@google.com9987ec32011-09-07 11:57:52 +00002212 if (dstY[1] > dstY[2]) {
2213 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2214 dstY[2] = dstY[1];
2215 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002216
reed@google.com9987ec32011-09-07 11:57:52 +00002217 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002218 SkRect s, d;
2219
reed@google.com9987ec32011-09-07 11:57:52 +00002220 s.fTop = srcY[y];
2221 s.fBottom = srcY[y+1];
2222 d.fTop = dstY[y];
2223 d.fBottom = dstY[y+1];
2224 for (int x = 0; x < 3; x++) {
2225 s.fLeft = srcX[x];
2226 s.fRight = srcX[x+1];
2227 d.fLeft = dstX[x];
2228 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002229 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002230 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002231 }
2232 }
2233}
2234
2235void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2236 const SkRect& dst, const SkPaint* paint) {
2237 SkDEBUGCODE(bitmap.validate();)
2238
2239 // Need a device entry-point, so gpu can use a mesh
2240 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2241}
2242
reed@google.comf67e4cf2011-03-15 20:56:58 +00002243class SkDeviceFilteredPaint {
2244public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002245 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2246 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002247 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002248 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002249 newPaint->setFlags(flags.fFlags);
2250 newPaint->setHinting(flags.fHinting);
2251 fPaint = newPaint;
2252 } else {
2253 fPaint = &paint;
2254 }
2255 }
2256
reed@google.comf67e4cf2011-03-15 20:56:58 +00002257 const SkPaint& paint() const { return *fPaint; }
2258
2259private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002260 const SkPaint* fPaint;
2261 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002262};
2263
bungeman@google.com52c748b2011-08-22 21:30:43 +00002264void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2265 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002266 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002267 draw.fDevice->drawRect(draw, r, paint);
2268 } else {
2269 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002270 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002271 draw.fDevice->drawRect(draw, r, p);
2272 }
2273}
2274
2275void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2276 const char text[], size_t byteLength,
2277 SkScalar x, SkScalar y) {
2278 SkASSERT(byteLength == 0 || text != NULL);
2279
2280 // nothing to draw
2281 if (text == NULL || byteLength == 0 ||
2282 draw.fClip->isEmpty() ||
2283 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2284 return;
2285 }
2286
2287 SkScalar width = 0;
2288 SkPoint start;
2289
2290 start.set(0, 0); // to avoid warning
2291 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2292 SkPaint::kStrikeThruText_Flag)) {
2293 width = paint.measureText(text, byteLength);
2294
2295 SkScalar offsetX = 0;
2296 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2297 offsetX = SkScalarHalf(width);
2298 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2299 offsetX = width;
2300 }
2301 start.set(x - offsetX, y);
2302 }
2303
2304 if (0 == width) {
2305 return;
2306 }
2307
2308 uint32_t flags = paint.getFlags();
2309
2310 if (flags & (SkPaint::kUnderlineText_Flag |
2311 SkPaint::kStrikeThruText_Flag)) {
2312 SkScalar textSize = paint.getTextSize();
2313 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2314 SkRect r;
2315
2316 r.fLeft = start.fX;
2317 r.fRight = start.fX + width;
2318
2319 if (flags & SkPaint::kUnderlineText_Flag) {
2320 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2321 start.fY);
2322 r.fTop = offset;
2323 r.fBottom = offset + height;
2324 DrawRect(draw, paint, r, textSize);
2325 }
2326 if (flags & SkPaint::kStrikeThruText_Flag) {
2327 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2328 start.fY);
2329 r.fTop = offset;
2330 r.fBottom = offset + height;
2331 DrawRect(draw, paint, r, textSize);
2332 }
2333 }
2334}
2335
reed@google.come0d9ce82014-04-23 04:00:17 +00002336void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2337 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002338 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002339
2340 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002341 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002342 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002343 DrawTextDecorations(iter, dfp.paint(),
2344 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345 }
2346
reed@google.com4e2b3d32011-04-07 14:18:59 +00002347 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348}
2349
reed@google.come0d9ce82014-04-23 04:00:17 +00002350void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2351 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002352 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002353
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002355 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002357 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002359
reed@google.com4e2b3d32011-04-07 14:18:59 +00002360 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002361}
2362
reed@google.come0d9ce82014-04-23 04:00:17 +00002363void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2364 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002365 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002366
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002368 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002370 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002372
reed@google.com4e2b3d32011-04-07 14:18:59 +00002373 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374}
2375
reed@google.come0d9ce82014-04-23 04:00:17 +00002376void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2377 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002378 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002379
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380 while (iter.next()) {
2381 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002382 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002384
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002385 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002386}
2387
reed@google.come0d9ce82014-04-23 04:00:17 +00002388// These will become non-virtual, so they always call the (virtual) onDraw... method
2389void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2390 const SkPaint& paint) {
2391 this->onDrawText(text, byteLength, x, y, paint);
2392}
2393void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2394 const SkPaint& paint) {
2395 this->onDrawPosText(text, byteLength, pos, paint);
2396}
2397void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2398 SkScalar constY, const SkPaint& paint) {
2399 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2400}
2401void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2402 const SkMatrix* matrix, const SkPaint& paint) {
2403 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2404}
2405
reed@android.com8a1c16f2008-12-17 15:59:43 +00002406void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2407 const SkPoint verts[], const SkPoint texs[],
2408 const SkColor colors[], SkXfermode* xmode,
2409 const uint16_t indices[], int indexCount,
2410 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002411 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002412
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413 while (iter.next()) {
2414 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002415 colors, xmode, indices, indexCount,
2416 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417 }
reed@google.com4b226022011-01-11 18:32:13 +00002418
reed@google.com4e2b3d32011-04-07 14:18:59 +00002419 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002420}
2421
2422//////////////////////////////////////////////////////////////////////////////
2423// These methods are NOT virtual, and therefore must call back into virtual
2424// methods, rather than actually drawing themselves.
2425//////////////////////////////////////////////////////////////////////////////
2426
2427void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002428 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002429 SkPaint paint;
2430
2431 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002432 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002433 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002434 }
2435 this->drawPaint(paint);
2436}
2437
reed@android.com845fdac2009-06-23 03:01:32 +00002438void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439 SkPaint paint;
2440
2441 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002442 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002443 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 }
2445 this->drawPaint(paint);
2446}
2447
2448void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2449 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002450
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451 pt.set(x, y);
2452 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2453}
2454
2455void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2456 SkPoint pt;
2457 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002458
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459 pt.set(x, y);
2460 paint.setColor(color);
2461 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2462}
2463
2464void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2465 const SkPaint& paint) {
2466 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002467
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468 pts[0].set(x0, y0);
2469 pts[1].set(x1, y1);
2470 this->drawPoints(kLines_PointMode, 2, pts, paint);
2471}
2472
2473void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2474 SkScalar right, SkScalar bottom,
2475 const SkPaint& paint) {
2476 SkRect r;
2477
2478 r.set(left, top, right, bottom);
2479 this->drawRect(r, paint);
2480}
2481
2482void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2483 const SkPaint& paint) {
2484 if (radius < 0) {
2485 radius = 0;
2486 }
2487
2488 SkRect r;
2489 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002490 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002491}
2492
2493void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2494 const SkPaint& paint) {
2495 if (rx > 0 && ry > 0) {
2496 if (paint.canComputeFastBounds()) {
2497 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002498 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002499 return;
2500 }
2501 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002502 SkRRect rrect;
2503 rrect.setRectXY(r, rx, ry);
2504 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 } else {
2506 this->drawRect(r, paint);
2507 }
2508}
2509
reed@android.com8a1c16f2008-12-17 15:59:43 +00002510void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2511 SkScalar sweepAngle, bool useCenter,
2512 const SkPaint& paint) {
2513 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2514 this->drawOval(oval, paint);
2515 } else {
2516 SkPath path;
2517 if (useCenter) {
2518 path.moveTo(oval.centerX(), oval.centerY());
2519 }
2520 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2521 if (useCenter) {
2522 path.close();
2523 }
2524 this->drawPath(path, paint);
2525 }
2526}
2527
2528void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2529 const SkPath& path, SkScalar hOffset,
2530 SkScalar vOffset, const SkPaint& paint) {
2531 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002532
reed@android.com8a1c16f2008-12-17 15:59:43 +00002533 matrix.setTranslate(hOffset, vOffset);
2534 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2535}
2536
reed@android.comf76bacf2009-05-13 14:00:33 +00002537///////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002538void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
2539 SkBaseDevice* device = this->getDevice();
2540 if (NULL != device) {
2541 device->EXPERIMENTAL_optimize(picture);
2542 }
2543}
reed@android.comf76bacf2009-05-13 14:00:33 +00002544
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002545void SkCanvas::EXPERIMENTAL_purge(SkPicture* picture) {
2546 SkBaseDevice* device = this->getTopDevice();
2547 if (NULL != device) {
2548 device->EXPERIMENTAL_purge(picture);
2549 }
2550}
2551
reed@android.com8a1c16f2008-12-17 15:59:43 +00002552void SkCanvas::drawPicture(SkPicture& picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002553 SkBaseDevice* device = this->getTopDevice();
2554 if (NULL != device) {
2555 // Canvas has to first give the device the opportunity to render
2556 // the picture itself.
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002557 if (device->EXPERIMENTAL_drawPicture(this, &picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002558 return; // the device has rendered the entire picture
2559 }
2560 }
2561
reed@android.com8a1c16f2008-12-17 15:59:43 +00002562 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002563}
2564
reed@android.com8a1c16f2008-12-17 15:59:43 +00002565///////////////////////////////////////////////////////////////////////////////
2566///////////////////////////////////////////////////////////////////////////////
2567
2568SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002569 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002570
2571 SkASSERT(canvas);
2572
2573 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2574 fDone = !fImpl->next();
2575}
2576
2577SkCanvas::LayerIter::~LayerIter() {
2578 fImpl->~SkDrawIter();
2579}
2580
2581void SkCanvas::LayerIter::next() {
2582 fDone = !fImpl->next();
2583}
2584
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002585SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002586 return fImpl->getDevice();
2587}
2588
2589const SkMatrix& SkCanvas::LayerIter::matrix() const {
2590 return fImpl->getMatrix();
2591}
2592
2593const SkPaint& SkCanvas::LayerIter::paint() const {
2594 const SkPaint* paint = fImpl->getPaint();
2595 if (NULL == paint) {
2596 paint = &fDefaultPaint;
2597 }
2598 return *paint;
2599}
2600
2601const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2602int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2603int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002604
2605///////////////////////////////////////////////////////////////////////////////
2606
2607SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002608
2609///////////////////////////////////////////////////////////////////////////////
2610
2611static bool supported_for_raster_canvas(const SkImageInfo& info) {
2612 switch (info.alphaType()) {
2613 case kPremul_SkAlphaType:
2614 case kOpaque_SkAlphaType:
2615 break;
2616 default:
2617 return false;
2618 }
2619
2620 switch (info.colorType()) {
2621 case kAlpha_8_SkColorType:
2622 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002623 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002624 break;
2625 default:
2626 return false;
2627 }
2628
2629 return true;
2630}
2631
2632SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2633 if (!supported_for_raster_canvas(info)) {
2634 return NULL;
2635 }
2636
2637 SkBitmap bitmap;
2638 if (!bitmap.allocPixels(info)) {
2639 return NULL;
2640 }
2641
2642 // should this functionality be moved into allocPixels()?
2643 if (!bitmap.info().isOpaque()) {
2644 bitmap.eraseColor(0);
2645 }
2646 return SkNEW_ARGS(SkCanvas, (bitmap));
2647}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002648
2649SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2650 if (!supported_for_raster_canvas(info)) {
2651 return NULL;
2652 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002653
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002654 SkBitmap bitmap;
2655 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2656 return NULL;
2657 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002658 return SkNEW_ARGS(SkCanvas, (bitmap));
2659}