blob: 0598528ba2c5805f8f2c9f339f3ae002d303f991 [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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000838 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 // call shared impl
840 return this->internalSave(flags);
841}
842
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000844#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000846#else
847 return true;
848#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849}
850
junov@chromium.orga907ac32012-02-24 21:54:07 +0000851bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000852 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000853 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000854 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000855 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000857 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000858
859 if (imageFilter) {
860 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
861 // Filters may grow the bounds beyond the device bounds.
862 op = SkRegion::kReplace_Op;
863 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000864 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 if (NULL != bounds) {
866 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000867
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 this->getTotalMatrix().mapRect(&r, *bounds);
869 r.roundOut(&ir);
870 // early exit if the layer's bounds are clipped out
871 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000872 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000873 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000874 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000875 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 }
877 } else { // no user bounds, so just use the clip
878 ir = clipBounds;
879 }
880
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000881 if (bounds_affects_clip(flags)) {
882 fClipStack.clipDevRect(ir, op);
883 // early exit if the clip is now empty
884 if (!fMCRec->fRasterClip->op(ir, op)) {
885 return false;
886 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000887 }
888
889 if (intersection) {
890 *intersection = ir;
891 }
892 return true;
893}
894
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000895SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) {
896
897 // Do nothing. Subclasses may do something.
898 return kFullLayer_SaveLayerStrategy;
899}
900
junov@chromium.orga907ac32012-02-24 21:54:07 +0000901int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
902 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000903 // Overriding classes may return false to signal that we don't need to create a layer.
904 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
905 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000906}
907
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000908static SkBaseDevice* create_compatible_device(SkCanvas* canvas,
909 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000910 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000911 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000912}
913
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000914int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
915 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000916#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
917 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
918#endif
919
junov@chromium.orga907ac32012-02-24 21:54:07 +0000920 // do this before we create the layer. We don't call the public save() since
921 // that would invoke a possibly overridden virtual
922 int count = this->internalSave(flags);
923
924 fDeviceCMDirty = true;
925
926 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000927 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 return count;
929 }
930
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000931 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
932 // the clipRectBounds() call above?
933 if (kNoLayer_SaveLayerStrategy == strategy) {
934 return count;
935 }
936
reed@google.comb55deeb2012-01-06 14:43:09 +0000937 // Kill the imagefilter if our device doesn't allow it
938 SkLazyPaint lazyP;
939 if (paint && paint->getImageFilter()) {
940 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000941 if (justForImageFilter) {
942 // early exit if the layer was just for the imageFilter
943 return count;
944 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000945 SkPaint* p = lazyP.set(*paint);
946 p->setImageFilter(NULL);
947 paint = p;
948 }
949 }
950
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000951 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
952 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
953 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000955 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000956 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000957 device = create_compatible_device(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000958 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000959 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000960 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000961 if (NULL == device) {
962 SkDebugf("Unable to create device for layer.");
963 return count;
964 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000965
reed@google.com6f8f2922011-03-04 22:27:10 +0000966 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000967 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 device->unref();
969
970 layer->fNext = fMCRec->fTopLayer;
971 fMCRec->fLayer = layer;
972 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
973
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000974 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 return count;
976}
977
978int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
979 SaveFlags flags) {
980 if (0xFF == alpha) {
981 return this->saveLayer(bounds, NULL, flags);
982 } else {
983 SkPaint tmpPaint;
984 tmpPaint.setAlpha(alpha);
985 return this->saveLayer(bounds, &tmpPaint, flags);
986 }
987}
988
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000989void SkCanvas::willRestore() {
990 // Do nothing. Subclasses may do something.
991}
992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993void SkCanvas::restore() {
994 // check for underflow
995 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000996 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 this->internalRestore();
998 }
999}
1000
1001void SkCanvas::internalRestore() {
1002 SkASSERT(fMCStack.count() != 0);
1003
1004 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001005 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001007 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
1008 fClipStack.restore();
1009 }
1010
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001011 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 DeviceCM* layer = fMCRec->fLayer; // may be null
1013 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1014 fMCRec->fLayer = NULL;
1015
1016 // now do the normal restore()
1017 fMCRec->~MCRec(); // balanced in save()
1018 fMCStack.pop_back();
1019 fMCRec = (MCRec*)fMCStack.back();
1020
1021 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1022 since if we're being recorded, we don't want to record this (the
1023 recorder will have already recorded the restore).
1024 */
1025 if (NULL != layer) {
1026 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001027 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001028 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1029 layer->fPaint);
1030 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001032
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001033 SkASSERT(fSaveLayerCount > 0);
1034 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 }
1036 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001037 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038}
1039
1040int SkCanvas::getSaveCount() const {
1041 return fMCStack.count();
1042}
1043
1044void SkCanvas::restoreToCount(int count) {
1045 // sanity check
1046 if (count < 1) {
1047 count = 1;
1048 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001049
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001050 int n = this->getSaveCount() - count;
1051 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 this->restore();
1053 }
1054}
1055
reed@google.com7c202932011-12-14 18:48:05 +00001056bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001057 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001058}
1059
reed@google.com76f10a32014-02-05 15:32:21 +00001060SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1061 return this->onNewSurface(info);
1062}
1063
1064SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1065 SkBaseDevice* dev = this->getDevice();
1066 return dev ? dev->newSurface(info) : NULL;
1067}
1068
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001069SkImageInfo SkCanvas::imageInfo() const {
1070 SkBaseDevice* dev = this->getDevice();
1071 if (dev) {
1072 return dev->imageInfo();
1073 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001074 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001075 }
1076}
1077
1078const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1079 return this->onPeekPixels(info, rowBytes);
1080}
1081
1082const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1083 SkBaseDevice* dev = this->getDevice();
1084 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1085}
1086
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001087void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1088 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1089 if (pixels && origin) {
1090 *origin = this->getTopDevice(false)->getOrigin();
1091 }
1092 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001093}
1094
1095void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1096 SkBaseDevice* dev = this->getTopDevice();
1097 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1098}
1099
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001100SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1101 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1102 if (NULL == fAddr) {
1103 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001104 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001105 return; // failure, fAddr is NULL
1106 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001107 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1108 return; // failure, fAddr is NULL
1109 }
1110 fAddr = fBitmap.getPixels();
1111 fRowBytes = fBitmap.rowBytes();
1112 }
1113 SkASSERT(fAddr); // success
1114}
1115
1116bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1117 if (fAddr) {
1118 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1119 NULL, NULL);
1120 } else {
1121 bitmap->reset();
1122 return false;
1123 }
1124}
1125
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001126void SkCanvas::onPushCull(const SkRect& cullRect) {
1127 // do nothing. Subclasses may do something
1128}
1129
1130void SkCanvas::onPopCull() {
1131 // do nothing. Subclasses may do something
1132}
1133
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001135#ifdef SK_DEBUG
1136// Ensure that cull rects are monotonically nested in device space.
1137void SkCanvas::validateCull(const SkIRect& devCull) {
1138 if (fCullStack.isEmpty()
1139 || devCull.isEmpty()
1140 || fCullStack.top().contains(devCull)) {
1141 return;
1142 }
1143
1144 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1145 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1146 fCullStack.top().x(), fCullStack.top().y(),
1147 fCullStack.top().right(), fCullStack.top().bottom()));
1148
1149#ifdef ASSERT_NESTED_CULLING
1150 SkDEBUGFAIL("Invalid cull.");
1151#endif
1152}
1153#endif
1154
1155void SkCanvas::pushCull(const SkRect& cullRect) {
1156 ++fCullCount;
1157 this->onPushCull(cullRect);
1158
1159#ifdef SK_DEBUG
1160 // Map the cull rect into device space.
1161 SkRect mappedCull;
1162 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1163
1164 // Take clipping into account.
1165 SkIRect devClip, devCull;
1166 mappedCull.roundOut(&devCull);
1167 this->getClipDeviceBounds(&devClip);
1168 if (!devCull.intersect(devClip)) {
1169 devCull.setEmpty();
1170 }
1171
1172 this->validateCull(devCull);
1173 fCullStack.push(devCull); // balanced in popCull
1174#endif
1175}
1176
1177void SkCanvas::popCull() {
1178 SkASSERT(fCullStack.count() == fCullCount);
1179
1180 if (fCullCount > 0) {
1181 --fCullCount;
1182 this->onPopCull();
1183
1184 SkDEBUGCODE(fCullStack.pop());
1185 }
1186}
1187
1188/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001190void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001192 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 return;
1194 }
1195
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001196 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001198 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001200
1201 SkDEBUGCODE(bitmap.validate();)
1202 CHECK_LOCKCOUNT_BALANCE(bitmap);
1203
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001204 SkRect storage;
1205 const SkRect* bounds = NULL;
1206 if (paint && paint->canComputeFastBounds()) {
1207 bitmap.getBounds(&storage);
1208 matrix.mapRect(&storage);
1209 bounds = &paint->computeFastBounds(storage, &storage);
1210 }
1211
1212 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001213
1214 while (iter.next()) {
1215 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1216 }
1217
1218 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219}
1220
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001221void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001222 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 SkPaint tmp;
1224 if (NULL == paint) {
1225 tmp.setDither(true);
1226 paint = &tmp;
1227 }
reed@google.com4b226022011-01-11 18:32:13 +00001228
reed@google.com8926b162012-03-23 15:36:36 +00001229 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001231 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001232 paint = &looper.paint();
1233 SkImageFilter* filter = paint->getImageFilter();
1234 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001235 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001236 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001237 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001238 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001239 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001240 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001241 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001242 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001243 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1244 SkAutoUnref aur(NULL);
1245 if (!cache) {
1246 cache = SkImageFilter::Cache::Create();
1247 aur.reset(cache);
1248 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001249 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001250 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001251 SkPaint tmpUnfiltered(*paint);
1252 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001253 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1254 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001255 }
1256 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001257 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001260 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261}
1262
reed@google.com8926b162012-03-23 15:36:36 +00001263void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1264 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001265 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001266 return;
1267 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001268 SkDEBUGCODE(bitmap.validate();)
1269 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001270
reed@google.com8926b162012-03-23 15:36:36 +00001271 SkPaint tmp;
1272 if (NULL == paint) {
1273 paint = &tmp;
1274 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001275
reed@google.com8926b162012-03-23 15:36:36 +00001276 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001277
reed@google.com8926b162012-03-23 15:36:36 +00001278 while (iter.next()) {
1279 paint = &looper.paint();
1280 SkImageFilter* filter = paint->getImageFilter();
1281 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1282 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001283 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001284 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001285 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001286 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001287 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001288 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001289 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1290 SkAutoUnref aur(NULL);
1291 if (!cache) {
1292 cache = SkImageFilter::Cache::Create();
1293 aur.reset(cache);
1294 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001295 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001296 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001297 SkPaint tmpUnfiltered(*paint);
1298 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001299 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001300 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001301 }
1302 } else {
1303 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1304 }
1305 }
1306 LOOPER_END
1307}
1308
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001310void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001311 SkMatrix m;
1312 m.setTranslate(dx, dy);
1313 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314}
1315
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001316void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001317 SkMatrix m;
1318 m.setScale(sx, sy);
1319 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320}
1321
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001322void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001323 SkMatrix m;
1324 m.setRotate(degrees);
1325 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326}
1327
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001328void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001329 SkMatrix m;
1330 m.setSkew(sx, sy);
1331 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001332}
1333
1334void SkCanvas::didConcat(const SkMatrix&) {
1335 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336}
1337
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001338void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001339 if (matrix.isIdentity()) {
1340 return;
1341 }
1342
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001344 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001345 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001346
1347 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001348}
1349
1350void SkCanvas::didSetMatrix(const SkMatrix&) {
1351 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352}
1353
1354void SkCanvas::setMatrix(const SkMatrix& matrix) {
1355 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001356 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001358 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359}
1360
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361void SkCanvas::resetMatrix() {
1362 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001363
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 matrix.reset();
1365 this->setMatrix(matrix);
1366}
1367
1368//////////////////////////////////////////////////////////////////////////////
1369
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001370void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001371 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1372 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001373}
1374
1375void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001376#ifdef SK_ENABLE_CLIP_QUICKREJECT
1377 if (SkRegion::kIntersect_Op == op) {
1378 if (fMCRec->fRasterClip->isEmpty()) {
1379 return false;
1380 }
1381
reed@google.com3b3e8952012-08-16 20:53:31 +00001382 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001383 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001384 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001385
1386 fClipStack.clipEmpty();
1387 return fMCRec->fRasterClip->setEmpty();
1388 }
1389 }
1390#endif
1391
reed@google.com5c3d1472011-02-22 19:12:23 +00001392 AutoValidateClip avc(this);
1393
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001395 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001396 if (!fAllowSoftClip) {
1397 edgeStyle = kHard_ClipEdgeStyle;
1398 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399
1400 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001401 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001402 // the matrix. This means we don't have to a) make a path, and b) tell
1403 // the region code to scan-convert the path, only to discover that it
1404 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
1407 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001408 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1409 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001411 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001412 // and clip against that, since it can handle any matrix. However, to
1413 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1414 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 SkPath path;
1416
1417 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001418 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 }
1420}
1421
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001422static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1423 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001424 // base is used to limit the size (and therefore memory allocation) of the
1425 // region that results from scan converting devPath.
1426 SkRegion base;
1427
reed@google.com819c9212011-02-23 18:56:55 +00001428 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001429 // since we are intersect, we can do better (tighter) with currRgn's
1430 // bounds, than just using the device. However, if currRgn is complex,
1431 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001432 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001433 // FIXME: we should also be able to do this when currClip->isBW(),
1434 // but relaxing the test above triggers GM asserts in
1435 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001436 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001437 } else {
reed@google.com00177082011-10-12 14:34:30 +00001438 base.setRect(currClip->getBounds());
1439 SkRasterClip clip;
1440 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001441 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001442 }
reed@google.com819c9212011-02-23 18:56:55 +00001443 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001444 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001445 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001446 currClip->setEmpty();
1447 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001448 }
1449
junov@chromium.orga907ac32012-02-24 21:54:07 +00001450 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001451
1452 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001453 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001454 } else {
reed@google.com00177082011-10-12 14:34:30 +00001455 SkRasterClip clip;
1456 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001457 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001458 }
1459 }
1460}
1461
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001462void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001463 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001464 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001465 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1466 } else {
1467 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001468 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001469}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001470
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001471void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001472 SkRRect transformedRRect;
1473 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1474 AutoValidateClip avc(this);
1475
1476 fDeviceCMDirty = true;
1477 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001478 if (!fAllowSoftClip) {
1479 edgeStyle = kHard_ClipEdgeStyle;
1480 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001481
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001482 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001483
1484 SkPath devPath;
1485 devPath.addRRect(transformedRRect);
1486
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1488 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001489 }
1490
1491 SkPath path;
1492 path.addRRect(rrect);
1493 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001494 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001495}
1496
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001497void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001498 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1499 SkRect r;
1500 if (!path.isInverseFillType() && path.isRect(&r)) {
1501 this->onClipRect(r, op, edgeStyle);
1502 } else {
1503 this->onClipPath(path, op, edgeStyle);
1504 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001505}
1506
1507void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001508#ifdef SK_ENABLE_CLIP_QUICKREJECT
1509 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1510 if (fMCRec->fRasterClip->isEmpty()) {
1511 return false;
1512 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001513
reed@google.com3b3e8952012-08-16 20:53:31 +00001514 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001515 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001516 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001517
reed@google.comda17f752012-08-16 18:27:05 +00001518 fClipStack.clipEmpty();
1519 return fMCRec->fRasterClip->setEmpty();
1520 }
1521 }
1522#endif
1523
reed@google.com5c3d1472011-02-22 19:12:23 +00001524 AutoValidateClip avc(this);
1525
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001527 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528 if (!fAllowSoftClip) {
1529 edgeStyle = kHard_ClipEdgeStyle;
1530 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531
1532 SkPath devPath;
1533 path.transform(*fMCRec->fMatrix, &devPath);
1534
reed@google.comfe701122011-11-08 19:41:23 +00001535 // Check if the transfomation, or the original path itself
1536 // made us empty. Note this can also happen if we contained NaN
1537 // values. computing the bounds detects this, and will set our
1538 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1539 if (devPath.getBounds().isEmpty()) {
1540 // resetting the path will remove any NaN or other wanky values
1541 // that might upset our scan converter.
1542 devPath.reset();
1543 }
1544
reed@google.com5c3d1472011-02-22 19:12:23 +00001545 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001546 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001547
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001548 if (fAllowSimplifyClip) {
1549 devPath.reset();
1550 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1551 const SkClipStack* clipStack = getClipStack();
1552 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1553 const SkClipStack::Element* element;
1554 while ((element = iter.next())) {
1555 SkClipStack::Element::Type type = element->getType();
1556 if (type == SkClipStack::Element::kEmpty_Type) {
1557 continue;
1558 }
1559 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001560 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001561 SkRegion::Op elementOp = element->getOp();
1562 if (elementOp == SkRegion::kReplace_Op) {
1563 devPath = operand;
1564 } else {
1565 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1566 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001567 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1568 // perhaps we need an API change to avoid this sort of mixed-signals about
1569 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001570 if (element->isAA()) {
1571 edgeStyle = kSoft_ClipEdgeStyle;
1572 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001573 }
1574 op = SkRegion::kReplace_Op;
1575 }
1576
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001577 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578}
1579
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001580void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001581 bool inverseFilled) {
1582 // This is for updating the clip conservatively using only bounds
1583 // information.
1584 // Contract:
1585 // The current clip must contain the true clip. The true
1586 // clip is the clip that would have normally been computed
1587 // by calls to clipPath and clipRRect
1588 // Objective:
1589 // Keep the current clip as small as possible without
1590 // breaking the contract, using only clip bounding rectangles
1591 // (for performance).
1592
1593 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1594 // don't have to worry about getting caught in a loop. Thus anywhere
1595 // we call a virtual method, we explicitly prefix it with
1596 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001597
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001598 if (inverseFilled) {
1599 switch (op) {
1600 case SkRegion::kIntersect_Op:
1601 case SkRegion::kDifference_Op:
1602 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001603 // the clip unchanged conservatively respects the contract.
1604 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001605 case SkRegion::kUnion_Op:
1606 case SkRegion::kReplace_Op:
1607 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001608 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001609 // These ops can grow the current clip up to the extents of
1610 // the input clip, which is inverse filled, so we just set
1611 // the current clip to the device bounds.
1612 SkRect deviceBounds;
1613 SkIRect deviceIBounds;
1614 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001615 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001616
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001617 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001618 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001619 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001620 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001622 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623 break;
1624 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001625 default:
1626 SkASSERT(0); // unhandled op?
1627 }
1628 } else {
1629 // Not inverse filled
1630 switch (op) {
1631 case SkRegion::kIntersect_Op:
1632 case SkRegion::kUnion_Op:
1633 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1635 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001636 case SkRegion::kDifference_Op:
1637 // Difference can only shrink the current clip.
1638 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001639 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001640 case SkRegion::kReverseDifference_Op:
1641 // To reverse, we swap in the bounds with a replace op.
1642 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001643 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1644 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001645 case SkRegion::kXOR_Op:
1646 // Be conservative, based on (A XOR B) always included in (A union B),
1647 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1649 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001650 default:
1651 SkASSERT(0); // unhandled op?
1652 }
1653 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001654}
1655
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001656void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001657 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001658}
1659
1660void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001661 AutoValidateClip avc(this);
1662
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001664 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665
reed@google.com5c3d1472011-02-22 19:12:23 +00001666 // todo: signal fClipStack that we have a region, and therefore (I guess)
1667 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001668 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001669
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001670 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671}
1672
reed@google.com819c9212011-02-23 18:56:55 +00001673#ifdef SK_DEBUG
1674void SkCanvas::validateClip() const {
1675 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001676 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001677 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001678 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001679 return;
1680 }
1681
reed@google.com819c9212011-02-23 18:56:55 +00001682 SkIRect ir;
1683 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001684 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001685
robertphillips@google.com80214e22012-07-20 15:33:18 +00001686 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001687 const SkClipStack::Element* element;
1688 while ((element = iter.next()) != NULL) {
1689 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001690 case SkClipStack::Element::kRect_Type:
1691 element->getRect().round(&ir);
1692 tmpClip.op(ir, element->getOp());
1693 break;
1694 case SkClipStack::Element::kEmpty_Type:
1695 tmpClip.setEmpty();
1696 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001697 default: {
1698 SkPath path;
1699 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001700 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001701 break;
1702 }
reed@google.com819c9212011-02-23 18:56:55 +00001703 }
1704 }
reed@google.com819c9212011-02-23 18:56:55 +00001705}
1706#endif
1707
reed@google.com90c07ea2012-04-13 13:50:27 +00001708void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001709 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001710 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001711
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001712 static const SkRect kEmpty = { 0, 0, 0, 0 };
1713 while ((element = iter.next()) != NULL) {
1714 switch (element->getType()) {
1715 case SkClipStack::Element::kPath_Type:
1716 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1717 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001718 case SkClipStack::Element::kRRect_Type:
1719 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1720 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001721 case SkClipStack::Element::kRect_Type:
1722 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1723 break;
1724 case SkClipStack::Element::kEmpty_Type:
1725 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1726 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001727 }
1728 }
1729}
1730
reed@google.com5c3d1472011-02-22 19:12:23 +00001731///////////////////////////////////////////////////////////////////////////////
1732
reed@google.com754de5f2014-02-24 19:38:20 +00001733bool SkCanvas::isClipEmpty() const {
1734 return fMCRec->fRasterClip->isEmpty();
1735}
1736
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001737bool SkCanvas::isClipRect() const {
1738 return fMCRec->fRasterClip->isRect();
1739}
1740
reed@google.com3b3e8952012-08-16 20:53:31 +00001741bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001742
reed@google.com16078632011-12-06 18:56:37 +00001743 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001744 return true;
1745
reed@google.com00177082011-10-12 14:34:30 +00001746 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747 return true;
1748 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749
tomhudson@google.com8d430182011-06-06 19:11:19 +00001750 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001751 SkRect dst;
1752 fMCRec->fMatrix->mapRect(&dst, rect);
1753 SkIRect idst;
1754 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001755 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001756 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001757 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001758
reed@android.coma380ae42009-07-21 01:17:02 +00001759 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001760 // TODO: should we use | instead, or compare all 4 at once?
1761 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001762 return true;
1763 }
reed@google.comc0784db2013-12-13 21:16:12 +00001764 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001765 return true;
1766 }
1767 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769}
1770
reed@google.com3b3e8952012-08-16 20:53:31 +00001771bool SkCanvas::quickReject(const SkPath& path) const {
1772 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773}
1774
reed@google.com3b3e8952012-08-16 20:53:31 +00001775bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001776 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001777 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778 return false;
1779 }
1780
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001781 SkMatrix inverse;
1782 // if we can't invert the CTM, we can't return local clip bounds
1783 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001784 if (bounds) {
1785 bounds->setEmpty();
1786 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001787 return false;
1788 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001790 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001791 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001792 // adjust it outwards in case we are antialiasing
1793 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001794
reed@google.com8f4d2302013-12-17 16:44:46 +00001795 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1796 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 inverse.mapRect(bounds, r);
1798 }
1799 return true;
1800}
1801
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001802bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001803 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001804 if (clip.isEmpty()) {
1805 if (bounds) {
1806 bounds->setEmpty();
1807 }
1808 return false;
1809 }
1810
1811 if (NULL != bounds) {
1812 *bounds = clip.getBounds();
1813 }
1814 return true;
1815}
1816
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817const SkMatrix& SkCanvas::getTotalMatrix() const {
1818 return *fMCRec->fMatrix;
1819}
1820
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001821#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001822SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001823 if (fMCRec->fRasterClip->isEmpty()) {
1824 return kEmpty_ClipType;
1825 }
1826 if (fMCRec->fRasterClip->isRect()) {
1827 return kRect_ClipType;
1828 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001829 return kComplex_ClipType;
1830}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001831#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001832
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001833#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001835 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001837#endif
1838
1839const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1840 return fMCRec->fRasterClip->forceGetBW();
1841}
1842
1843void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1844 path->reset();
1845
1846 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1847 if (rgn.isEmpty()) {
1848 return;
1849 }
1850 (void)rgn.getBoundaryPath(path);
1851}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852
reed@google.com9c135db2014-03-12 18:28:35 +00001853GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1854 SkBaseDevice* dev = this->getTopDevice();
1855 return dev ? dev->accessRenderTarget() : NULL;
1856}
1857
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001858SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001859 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001860 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861}
1862
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001863GrContext* SkCanvas::getGrContext() {
1864#if SK_SUPPORT_GPU
1865 SkBaseDevice* device = this->getTopDevice();
1866 if (NULL != device) {
1867 GrRenderTarget* renderTarget = device->accessRenderTarget();
1868 if (NULL != renderTarget) {
1869 return renderTarget->getContext();
1870 }
1871 }
1872#endif
1873
1874 return NULL;
1875
1876}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001877
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001878void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1879 const SkPaint& paint) {
1880 if (outer.isEmpty()) {
1881 return;
1882 }
1883 if (inner.isEmpty()) {
1884 this->drawRRect(outer, paint);
1885 return;
1886 }
1887
1888 // We don't have this method (yet), but technically this is what we should
1889 // be able to assert...
1890 // SkASSERT(outer.contains(inner));
1891 //
1892 // For now at least check for containment of bounds
1893 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1894
1895 this->onDrawDRRect(outer, inner, paint);
1896}
1897
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898//////////////////////////////////////////////////////////////////////////////
1899// These are the virtual drawing methods
1900//////////////////////////////////////////////////////////////////////////////
1901
reed@google.com2a981812011-04-14 18:59:28 +00001902void SkCanvas::clear(SkColor color) {
1903 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001904 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001905 while (iter.next()) {
1906 iter.fDevice->clear(color);
1907 }
1908}
1909
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001910void SkCanvas::onDiscard() {
1911 if (NULL != fSurfaceBase) {
1912 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1913 }
1914}
1915
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001917 this->internalDrawPaint(paint);
1918}
1919
1920void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001921 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001922
1923 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001924 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 }
1926
reed@google.com4e2b3d32011-04-07 14:18:59 +00001927 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928}
1929
1930void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1931 const SkPaint& paint) {
1932 if ((long)count <= 0) {
1933 return;
1934 }
1935
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001936 SkRect r, storage;
1937 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001938 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001939 // special-case 2 points (common for drawing a single line)
1940 if (2 == count) {
1941 r.set(pts[0], pts[1]);
1942 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001943 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001944 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001945 bounds = &paint.computeFastStrokeBounds(r, &storage);
1946 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001947 return;
1948 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001949 }
reed@google.coma584aed2012-05-16 14:06:02 +00001950
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 SkASSERT(pts != NULL);
1952
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001953 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001954
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001956 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957 }
reed@google.com4b226022011-01-11 18:32:13 +00001958
reed@google.com4e2b3d32011-04-07 14:18:59 +00001959 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001960}
1961
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001962void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001963 SkRect storage;
1964 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001966 bounds = &paint.computeFastBounds(r, &storage);
1967 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968 return;
1969 }
1970 }
reed@google.com4b226022011-01-11 18:32:13 +00001971
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001972 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973
1974 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001975 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976 }
1977
reed@google.com4e2b3d32011-04-07 14:18:59 +00001978 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979}
1980
reed@google.com4ed0fb72012-12-12 20:48:18 +00001981void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001982 SkRect storage;
1983 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001984 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001985 bounds = &paint.computeFastBounds(oval, &storage);
1986 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001987 return;
1988 }
1989 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001990
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001991 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001992
1993 while (iter.next()) {
1994 iter.fDevice->drawOval(iter, oval, looper.paint());
1995 }
1996
1997 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001998}
1999
2000void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002001 SkRect storage;
2002 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002003 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002004 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2005 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002006 return;
2007 }
2008 }
2009
2010 if (rrect.isRect()) {
2011 // call the non-virtual version
2012 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002013 return;
2014 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002015 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002016 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2017 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002018 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002019
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002020 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002021
2022 while (iter.next()) {
2023 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2024 }
2025
2026 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002027}
2028
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002029void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2030 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002031 SkRect storage;
2032 const SkRect* bounds = NULL;
2033 if (paint.canComputeFastBounds()) {
2034 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2035 if (this->quickReject(*bounds)) {
2036 return;
2037 }
2038 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002039
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002040 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002041
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002042 while (iter.next()) {
2043 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2044 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002045
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002046 LOOPER_END
2047}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002048
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00002049void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002050 if (!path.isFinite()) {
2051 return;
2052 }
2053
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002054 SkRect storage;
2055 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002056 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002057 const SkRect& pathBounds = path.getBounds();
2058 bounds = &paint.computeFastBounds(pathBounds, &storage);
2059 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002060 return;
2061 }
2062 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002063
2064 const SkRect& r = path.getBounds();
2065 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002066 if (path.isInverseFillType()) {
2067 this->internalDrawPaint(paint);
2068 }
2069 return;
2070 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002072 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073
2074 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002075 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076 }
2077
reed@google.com4e2b3d32011-04-07 14:18:59 +00002078 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002079}
2080
2081void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2082 const SkPaint* paint) {
2083 SkDEBUGCODE(bitmap.validate();)
2084
reed@google.com3d608122011-11-21 15:16:16 +00002085 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002086 SkRect bounds = {
2087 x, y,
2088 x + SkIntToScalar(bitmap.width()),
2089 y + SkIntToScalar(bitmap.height())
2090 };
2091 if (paint) {
2092 (void)paint->computeFastBounds(bounds, &bounds);
2093 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002094 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 return;
2096 }
2097 }
reed@google.com4b226022011-01-11 18:32:13 +00002098
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 SkMatrix matrix;
2100 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002101 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102}
2103
reed@google.com9987ec32011-09-07 11:57:52 +00002104// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002105void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002106 const SkRect& dst, const SkPaint* paint,
2107 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002108 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109 return;
2110 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002111
reed@google.comea033602012-12-14 13:13:55 +00002112 CHECK_LOCKCOUNT_BALANCE(bitmap);
2113
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002114 SkRect storage;
2115 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002116 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002117 if (paint) {
2118 bounds = &paint->computeFastBounds(dst, &storage);
2119 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002120 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002121 return;
2122 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002123 }
reed@google.com3d608122011-11-21 15:16:16 +00002124
reed@google.com33535f32012-09-25 15:37:50 +00002125 SkLazyPaint lazy;
2126 if (NULL == paint) {
2127 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002129
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002130 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002131
reed@google.com33535f32012-09-25 15:37:50 +00002132 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002133 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002134 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002135
reed@google.com33535f32012-09-25 15:37:50 +00002136 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137}
2138
reed@google.com71121732012-09-18 15:14:33 +00002139void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002140 const SkRect& dst, const SkPaint* paint,
2141 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002142 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002143 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002144}
2145
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2147 const SkPaint* paint) {
2148 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002149 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150}
2151
reed@google.com9987ec32011-09-07 11:57:52 +00002152void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2153 const SkIRect& center, const SkRect& dst,
2154 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002155 if (bitmap.drawsNothing()) {
2156 return;
2157 }
reed@google.com3d608122011-11-21 15:16:16 +00002158 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002159 SkRect storage;
2160 const SkRect* bounds = &dst;
2161 if (paint) {
2162 bounds = &paint->computeFastBounds(dst, &storage);
2163 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002164 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002165 return;
2166 }
2167 }
2168
reed@google.com9987ec32011-09-07 11:57:52 +00002169 const int32_t w = bitmap.width();
2170 const int32_t h = bitmap.height();
2171
2172 SkIRect c = center;
2173 // pin center to the bounds of the bitmap
2174 c.fLeft = SkMax32(0, center.fLeft);
2175 c.fTop = SkMax32(0, center.fTop);
2176 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2177 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2178
reed@google.com71121732012-09-18 15:14:33 +00002179 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002180 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002181 };
2182 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002183 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002184 };
reed@google.com9987ec32011-09-07 11:57:52 +00002185 SkScalar dstX[4] = {
2186 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2187 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2188 };
2189 SkScalar dstY[4] = {
2190 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2191 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2192 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002193
reed@google.com9987ec32011-09-07 11:57:52 +00002194 if (dstX[1] > dstX[2]) {
2195 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2196 dstX[2] = dstX[1];
2197 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002198
reed@google.com9987ec32011-09-07 11:57:52 +00002199 if (dstY[1] > dstY[2]) {
2200 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2201 dstY[2] = dstY[1];
2202 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002203
reed@google.com9987ec32011-09-07 11:57:52 +00002204 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002205 SkRect s, d;
2206
reed@google.com9987ec32011-09-07 11:57:52 +00002207 s.fTop = srcY[y];
2208 s.fBottom = srcY[y+1];
2209 d.fTop = dstY[y];
2210 d.fBottom = dstY[y+1];
2211 for (int x = 0; x < 3; x++) {
2212 s.fLeft = srcX[x];
2213 s.fRight = srcX[x+1];
2214 d.fLeft = dstX[x];
2215 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002216 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002217 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002218 }
2219 }
2220}
2221
2222void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2223 const SkRect& dst, const SkPaint* paint) {
2224 SkDEBUGCODE(bitmap.validate();)
2225
2226 // Need a device entry-point, so gpu can use a mesh
2227 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2228}
2229
reed@google.comf67e4cf2011-03-15 20:56:58 +00002230class SkDeviceFilteredPaint {
2231public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002232 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2233 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002234 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002235 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002236 newPaint->setFlags(flags.fFlags);
2237 newPaint->setHinting(flags.fHinting);
2238 fPaint = newPaint;
2239 } else {
2240 fPaint = &paint;
2241 }
2242 }
2243
reed@google.comf67e4cf2011-03-15 20:56:58 +00002244 const SkPaint& paint() const { return *fPaint; }
2245
2246private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002247 const SkPaint* fPaint;
2248 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002249};
2250
bungeman@google.com52c748b2011-08-22 21:30:43 +00002251void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2252 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002253 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002254 draw.fDevice->drawRect(draw, r, paint);
2255 } else {
2256 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002257 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002258 draw.fDevice->drawRect(draw, r, p);
2259 }
2260}
2261
2262void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2263 const char text[], size_t byteLength,
2264 SkScalar x, SkScalar y) {
2265 SkASSERT(byteLength == 0 || text != NULL);
2266
2267 // nothing to draw
2268 if (text == NULL || byteLength == 0 ||
2269 draw.fClip->isEmpty() ||
2270 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2271 return;
2272 }
2273
2274 SkScalar width = 0;
2275 SkPoint start;
2276
2277 start.set(0, 0); // to avoid warning
2278 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2279 SkPaint::kStrikeThruText_Flag)) {
2280 width = paint.measureText(text, byteLength);
2281
2282 SkScalar offsetX = 0;
2283 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2284 offsetX = SkScalarHalf(width);
2285 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2286 offsetX = width;
2287 }
2288 start.set(x - offsetX, y);
2289 }
2290
2291 if (0 == width) {
2292 return;
2293 }
2294
2295 uint32_t flags = paint.getFlags();
2296
2297 if (flags & (SkPaint::kUnderlineText_Flag |
2298 SkPaint::kStrikeThruText_Flag)) {
2299 SkScalar textSize = paint.getTextSize();
2300 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2301 SkRect r;
2302
2303 r.fLeft = start.fX;
2304 r.fRight = start.fX + width;
2305
2306 if (flags & SkPaint::kUnderlineText_Flag) {
2307 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2308 start.fY);
2309 r.fTop = offset;
2310 r.fBottom = offset + height;
2311 DrawRect(draw, paint, r, textSize);
2312 }
2313 if (flags & SkPaint::kStrikeThruText_Flag) {
2314 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2315 start.fY);
2316 r.fTop = offset;
2317 r.fBottom = offset + height;
2318 DrawRect(draw, paint, r, textSize);
2319 }
2320 }
2321}
2322
reed@google.come0d9ce82014-04-23 04:00:17 +00002323void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2324 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002325 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002326
2327 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002328 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002329 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002330 DrawTextDecorations(iter, dfp.paint(),
2331 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002332 }
2333
reed@google.com4e2b3d32011-04-07 14:18:59 +00002334 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002335}
2336
reed@google.come0d9ce82014-04-23 04:00:17 +00002337void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2338 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002339 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002340
reed@android.com8a1c16f2008-12-17 15:59:43 +00002341 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002342 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002344 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002346
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::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2351 SkScalar constY, 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, xpos, constY, 1,
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::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2364 const SkMatrix* matrix, 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()) {
2368 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002369 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002371
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002372 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002373}
2374
reed@google.come0d9ce82014-04-23 04:00:17 +00002375// These will become non-virtual, so they always call the (virtual) onDraw... method
2376void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2377 const SkPaint& paint) {
2378 this->onDrawText(text, byteLength, x, y, paint);
2379}
2380void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2381 const SkPaint& paint) {
2382 this->onDrawPosText(text, byteLength, pos, paint);
2383}
2384void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2385 SkScalar constY, const SkPaint& paint) {
2386 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2387}
2388void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2389 const SkMatrix* matrix, const SkPaint& paint) {
2390 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2391}
2392
reed@android.com8a1c16f2008-12-17 15:59:43 +00002393void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2394 const SkPoint verts[], const SkPoint texs[],
2395 const SkColor colors[], SkXfermode* xmode,
2396 const uint16_t indices[], int indexCount,
2397 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002398 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002399
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400 while (iter.next()) {
2401 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002402 colors, xmode, indices, indexCount,
2403 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002404 }
reed@google.com4b226022011-01-11 18:32:13 +00002405
reed@google.com4e2b3d32011-04-07 14:18:59 +00002406 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407}
2408
2409//////////////////////////////////////////////////////////////////////////////
2410// These methods are NOT virtual, and therefore must call back into virtual
2411// methods, rather than actually drawing themselves.
2412//////////////////////////////////////////////////////////////////////////////
2413
2414void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002415 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 SkPaint paint;
2417
2418 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002419 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002420 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002421 }
2422 this->drawPaint(paint);
2423}
2424
reed@android.com845fdac2009-06-23 03:01:32 +00002425void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426 SkPaint paint;
2427
2428 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002429 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002430 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 }
2432 this->drawPaint(paint);
2433}
2434
2435void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2436 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002437
reed@android.com8a1c16f2008-12-17 15:59:43 +00002438 pt.set(x, y);
2439 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2440}
2441
2442void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2443 SkPoint pt;
2444 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002445
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446 pt.set(x, y);
2447 paint.setColor(color);
2448 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2449}
2450
2451void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2452 const SkPaint& paint) {
2453 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002454
reed@android.com8a1c16f2008-12-17 15:59:43 +00002455 pts[0].set(x0, y0);
2456 pts[1].set(x1, y1);
2457 this->drawPoints(kLines_PointMode, 2, pts, paint);
2458}
2459
2460void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2461 SkScalar right, SkScalar bottom,
2462 const SkPaint& paint) {
2463 SkRect r;
2464
2465 r.set(left, top, right, bottom);
2466 this->drawRect(r, paint);
2467}
2468
2469void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2470 const SkPaint& paint) {
2471 if (radius < 0) {
2472 radius = 0;
2473 }
2474
2475 SkRect r;
2476 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002477 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478}
2479
2480void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2481 const SkPaint& paint) {
2482 if (rx > 0 && ry > 0) {
2483 if (paint.canComputeFastBounds()) {
2484 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002485 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002486 return;
2487 }
2488 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002489 SkRRect rrect;
2490 rrect.setRectXY(r, rx, ry);
2491 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002492 } else {
2493 this->drawRect(r, paint);
2494 }
2495}
2496
reed@android.com8a1c16f2008-12-17 15:59:43 +00002497void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2498 SkScalar sweepAngle, bool useCenter,
2499 const SkPaint& paint) {
2500 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2501 this->drawOval(oval, paint);
2502 } else {
2503 SkPath path;
2504 if (useCenter) {
2505 path.moveTo(oval.centerX(), oval.centerY());
2506 }
2507 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2508 if (useCenter) {
2509 path.close();
2510 }
2511 this->drawPath(path, paint);
2512 }
2513}
2514
2515void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2516 const SkPath& path, SkScalar hOffset,
2517 SkScalar vOffset, const SkPaint& paint) {
2518 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002519
reed@android.com8a1c16f2008-12-17 15:59:43 +00002520 matrix.setTranslate(hOffset, vOffset);
2521 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2522}
2523
reed@android.comf76bacf2009-05-13 14:00:33 +00002524///////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002525void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
2526 SkBaseDevice* device = this->getDevice();
2527 if (NULL != device) {
2528 device->EXPERIMENTAL_optimize(picture);
2529 }
2530}
reed@android.comf76bacf2009-05-13 14:00:33 +00002531
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002532void SkCanvas::EXPERIMENTAL_purge(SkPicture* picture) {
2533 SkBaseDevice* device = this->getTopDevice();
2534 if (NULL != device) {
2535 device->EXPERIMENTAL_purge(picture);
2536 }
2537}
2538
reed@android.com8a1c16f2008-12-17 15:59:43 +00002539void SkCanvas::drawPicture(SkPicture& picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002540 SkBaseDevice* device = this->getTopDevice();
2541 if (NULL != device) {
2542 // Canvas has to first give the device the opportunity to render
2543 // the picture itself.
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002544 if (device->EXPERIMENTAL_drawPicture(this, &picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002545 return; // the device has rendered the entire picture
2546 }
2547 }
2548
reed@android.com8a1c16f2008-12-17 15:59:43 +00002549 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002550}
2551
reed@android.com8a1c16f2008-12-17 15:59:43 +00002552///////////////////////////////////////////////////////////////////////////////
2553///////////////////////////////////////////////////////////////////////////////
2554
2555SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002556 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002557
2558 SkASSERT(canvas);
2559
2560 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2561 fDone = !fImpl->next();
2562}
2563
2564SkCanvas::LayerIter::~LayerIter() {
2565 fImpl->~SkDrawIter();
2566}
2567
2568void SkCanvas::LayerIter::next() {
2569 fDone = !fImpl->next();
2570}
2571
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002572SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002573 return fImpl->getDevice();
2574}
2575
2576const SkMatrix& SkCanvas::LayerIter::matrix() const {
2577 return fImpl->getMatrix();
2578}
2579
2580const SkPaint& SkCanvas::LayerIter::paint() const {
2581 const SkPaint* paint = fImpl->getPaint();
2582 if (NULL == paint) {
2583 paint = &fDefaultPaint;
2584 }
2585 return *paint;
2586}
2587
2588const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2589int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2590int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002591
2592///////////////////////////////////////////////////////////////////////////////
2593
2594SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002595
2596///////////////////////////////////////////////////////////////////////////////
2597
2598static bool supported_for_raster_canvas(const SkImageInfo& info) {
2599 switch (info.alphaType()) {
2600 case kPremul_SkAlphaType:
2601 case kOpaque_SkAlphaType:
2602 break;
2603 default:
2604 return false;
2605 }
2606
2607 switch (info.colorType()) {
2608 case kAlpha_8_SkColorType:
2609 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002610 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002611 break;
2612 default:
2613 return false;
2614 }
2615
2616 return true;
2617}
2618
2619SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2620 if (!supported_for_raster_canvas(info)) {
2621 return NULL;
2622 }
2623
2624 SkBitmap bitmap;
2625 if (!bitmap.allocPixels(info)) {
2626 return NULL;
2627 }
2628
2629 // should this functionality be moved into allocPixels()?
2630 if (!bitmap.info().isOpaque()) {
2631 bitmap.eraseColor(0);
2632 }
2633 return SkNEW_ARGS(SkCanvas, (bitmap));
2634}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002635
2636SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2637 if (!supported_for_raster_canvas(info)) {
2638 return NULL;
2639 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002640
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002641 SkBitmap bitmap;
2642 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2643 return NULL;
2644 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002645 return SkNEW_ARGS(SkCanvas, (bitmap));
2646}