blob: 90c8ec82670d0cc34342c5569cee3b97fe3efc95 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
reed9f014712014-06-18 15:51:20 -07008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000011#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
13#include "SkDrawFilter.h"
14#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000015#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000016#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000018#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000019#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000020#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000021#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000023#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000024#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000027#if SK_SUPPORT_GPU
28#include "GrRenderTarget.h"
29#endif
30
reed@google.comda17f752012-08-16 18:27:05 +000031// experimental for faster tiled drawing...
32//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000033
reed@android.com8a1c16f2008-12-17 15:59:43 +000034//#define SK_TRACE_SAVERESTORE
35
36#ifdef SK_TRACE_SAVERESTORE
37 static int gLayerCounter;
38 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
39 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
40
41 static int gRecCounter;
42 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
43 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
44
45 static int gCanvasCounter;
46 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
47 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
48#else
49 #define inc_layer()
50 #define dec_layer()
51 #define inc_rec()
52 #define dec_rec()
53 #define inc_canvas()
54 #define dec_canvas()
55#endif
56
reed@google.comea033602012-12-14 13:13:55 +000057#ifdef SK_DEBUG
58#include "SkPixelRef.h"
59
reed@google.comf53d0a92013-01-30 13:17:32 +000060/*
61 * Some pixelref subclasses can support being "locked" from another thread
62 * during the lock-scope of skia calling them. In these instances, this balance
63 * check will fail, but may not be indicative of a problem, so we allow a build
64 * flag to disable this check.
65 *
66 * Potentially another fix would be to have a (debug-only) virtual or flag on
67 * pixelref, which could tell us at runtime if this check is valid. That would
68 * eliminate the need for this heavy-handed build check.
69 */
70#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
71class AutoCheckLockCountBalance {
72public:
73 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
74};
75#else
reed@google.comea033602012-12-14 13:13:55 +000076class AutoCheckLockCountBalance {
77public:
78 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
79 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
80 }
81 ~AutoCheckLockCountBalance() {
82 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
83 SkASSERT(count == fLockCount);
84 }
85
86private:
87 const SkPixelRef* fPixelRef;
88 int fLockCount;
89};
reed@google.comf53d0a92013-01-30 13:17:32 +000090#endif
reed@google.comea033602012-12-14 13:13:55 +000091
reed@google.comea033602012-12-14 13:13:55 +000092#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000093
94#else
95 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000096#endif
97
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000098typedef SkTLazy<SkPaint> SkLazyPaint;
99
reed@google.com97af1a62012-08-28 12:19:02 +0000100void SkCanvas::predrawNotify() {
101 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000102 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000103 }
104}
105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000108/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 The clip/matrix/proc are fields that reflect the top of the save/restore
110 stack. Whenever the canvas changes, it marks a dirty flag, and then before
111 these are used (assuming we're not on a layer) we rebuild these cache
112 values: they reflect the top of the save stack, but translated and clipped
113 by the device's XY offset and bitmap-bounds.
114*/
115struct DeviceCM {
116 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000117 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000118 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000120 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000122 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 : fNext(NULL) {
124 if (NULL != device) {
125 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000126 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000130 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000132 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000134 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 fDevice->unref();
136 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000137 SkDELETE(fPaint);
138 }
reed@google.com4b226022011-01-11 18:32:13 +0000139
reed@google.com045e62d2011-10-24 12:19:46 +0000140 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
141 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000142 int x = fDevice->getOrigin().x();
143 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 int width = fDevice->width();
145 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 if ((x | y) == 0) {
148 fMatrix = &totalMatrix;
149 fClip = totalClip;
150 } else {
151 fMatrixStorage = totalMatrix;
152 fMatrixStorage.postTranslate(SkIntToScalar(-x),
153 SkIntToScalar(-y));
154 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000155
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 totalClip.translate(-x, -y, &fClip);
157 }
158
reed@google.com045e62d2011-10-24 12:19:46 +0000159 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160
161 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000162
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000164 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 SkRegion::kDifference_Op);
166 }
reed@google.com4b226022011-01-11 18:32:13 +0000167
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000168 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170#ifdef SK_DEBUG
171 if (!fClip.isEmpty()) {
172 SkIRect deviceR;
173 deviceR.set(0, 0, width, height);
174 SkASSERT(deviceR.contains(fClip.getBounds()));
175 }
176#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000177 }
178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000180 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181};
182
183/* This is the record we keep for each save/restore level in the stack.
184 Since a level optionally copies the matrix and/or stack, we have pointers
185 for these fields. If the value is copied for this level, the copy is
186 stored in the ...Storage field, and the pointer points to that. If the
187 value is not copied for this level, we ignore ...Storage, and just point
188 at the corresponding value in the previous level in the stack.
189*/
190class SkCanvas::MCRec {
191public:
reed1f836ee2014-07-07 07:49:34 -0700192 SkMatrix fMatrix;
193 SkRasterClip fRasterClip;
194 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000195
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 DeviceCM* fLayer;
197 /* If there are any layers in the stack, this points to the top-most
198 one that is at or below this level in the stack (so we know what
199 bitmap/device to draw into from this level. This value is NOT
200 reference counted, since the real owner is either our fLayer field,
201 or a previous one in a lower level.)
202 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000203 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
Florin Malita5f6102d2014-06-30 10:13:28 -0400205 MCRec(const MCRec* prev) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 if (NULL != prev) {
reed1f836ee2014-07-07 07:49:34 -0700207 fMatrix = prev->fMatrix;
208 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209
210 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000211 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212
213 fTopLayer = prev->fTopLayer;
214 } else { // no prev
reed1f836ee2014-07-07 07:49:34 -0700215 fMatrix.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 fFilter = NULL;
217 fTopLayer = NULL;
218 }
219 fLayer = NULL;
220
221 // don't bother initializing fNext
222 inc_rec();
223 }
224 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000225 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 SkDELETE(fLayer);
227 dec_rec();
228 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229};
230
231class SkDrawIter : public SkDraw {
232public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000233 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000234 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000235 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 canvas->updateDeviceCMCache();
237
reed@google.com90c07ea2012-04-13 13:50:27 +0000238 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000240 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 }
reed@google.com4b226022011-01-11 18:32:13 +0000242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 bool next() {
244 // skip over recs with empty clips
245 if (fSkipEmptyClips) {
246 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
247 fCurrLayer = fCurrLayer->fNext;
248 }
249 }
250
reed@google.comf68c5e22012-02-24 16:38:58 +0000251 const DeviceCM* rec = fCurrLayer;
252 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
254 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000255 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
256 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 fDevice = rec->fDevice;
258 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000260 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261
262 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 return true;
266 }
267 return false;
268 }
reed@google.com4b226022011-01-11 18:32:13 +0000269
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000270 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000271 int getX() const { return fDevice->getOrigin().x(); }
272 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 const SkMatrix& getMatrix() const { return *fMatrix; }
274 const SkRegion& getClip() const { return *fClip; }
275 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000276
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277private:
278 SkCanvas* fCanvas;
279 const DeviceCM* fCurrLayer;
280 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 SkBool8 fSkipEmptyClips;
282
283 typedef SkDraw INHERITED;
284};
285
286/////////////////////////////////////////////////////////////////////////////
287
288class AutoDrawLooper {
289public:
reed@google.com8926b162012-03-23 15:36:36 +0000290 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000291 bool skipLayerForImageFilter = false,
292 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000295 fPaint = NULL;
296 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000297 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000298 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299
reed@google.com8926b162012-03-23 15:36:36 +0000300 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
301 SkPaint tmp;
302 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000303 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
304 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000305 // we'll clear the imageFilter for the actual draws in next(), so
306 // it will only be applied during the restore().
307 fDoClearImageFilter = true;
308 }
309
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000310 if (SkDrawLooper* looper = paint.getLooper()) {
311 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
312 looper->contextSize());
313 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000314 fIsSimple = false;
315 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000316 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000317 // can we be marked as simple?
318 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000319 }
320 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000321
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000323 if (fDoClearImageFilter) {
324 fCanvas->internalRestore();
325 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000326 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000328
reed@google.com4e2b3d32011-04-07 14:18:59 +0000329 const SkPaint& paint() const {
330 SkASSERT(fPaint);
331 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000333
reed@google.com129ec222012-05-15 13:24:09 +0000334 bool next(SkDrawFilter::Type drawType) {
335 if (fDone) {
336 return false;
337 } else if (fIsSimple) {
338 fDone = true;
339 fPaint = &fOrigPaint;
340 return !fPaint->nothingToDraw();
341 } else {
342 return this->doNext(drawType);
343 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000344 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000345
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000347 SkLazyPaint fLazyPaint;
348 SkCanvas* fCanvas;
349 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000350 SkDrawFilter* fFilter;
351 const SkPaint* fPaint;
352 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000353 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000354 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000355 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000356 SkDrawLooper::Context* fLooperContext;
357 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000358
359 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360};
361
reed@google.com129ec222012-05-15 13:24:09 +0000362bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000363 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000364 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000365 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000366
367 SkPaint* paint = fLazyPaint.set(fOrigPaint);
368
369 if (fDoClearImageFilter) {
370 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000371 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000372
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000373 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000375 return false;
376 }
377 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000378 if (!fFilter->filter(paint, drawType)) {
379 fDone = true;
380 return false;
381 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000382 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000383 // no looper means we only draw once
384 fDone = true;
385 }
386 }
387 fPaint = paint;
388
389 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000390 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000391 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000392 }
393
394 // call this after any possible paint modifiers
395 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000396 fPaint = NULL;
397 return false;
398 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000399 return true;
400}
401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402#include "SkColorPriv.h"
403
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404////////// macros to place around the internal draw calls //////////////////
405
reed@google.com8926b162012-03-23 15:36:36 +0000406#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000407 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000408 AutoDrawLooper looper(this, paint, true); \
409 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000410 SkDrawIter iter(this);
411
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000412#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000413 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000414 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000417
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419
420////////////////////////////////////////////////////////////////////////////
421
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000422SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000423 fCachedLocalClipBounds.setEmpty();
424 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000425 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000426 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000427 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000428 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000429 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000430 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431
432 fMCRec = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400433 new (fMCRec) MCRec(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000435 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437
reed@google.com97af1a62012-08-28 12:19:02 +0000438 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000439
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000440 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441}
442
reed@google.comcde92112011-07-06 20:00:52 +0000443SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000444 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
445{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000446 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000447
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000448 this->init(NULL);
449}
450
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000451SkCanvas::SkCanvas(int width, int height)
452 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
453{
454 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000455
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000456 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000457 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000458 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
459}
460
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000461SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000462 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
463{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 inc_canvas();
465
466 this->init(device);
467}
468
469SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000470 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
471{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 inc_canvas();
473
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000474 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475}
476
477SkCanvas::~SkCanvas() {
478 // free up the contents of our deque
479 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000480 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000481
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 this->internalRestore(); // restore the last, since we're going away
483
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000484 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000485
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 dec_canvas();
487}
488
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489SkDrawFilter* SkCanvas::getDrawFilter() const {
490 return fMCRec->fFilter;
491}
492
493SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
494 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
495 return filter;
496}
497
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000498SkMetaData& SkCanvas::getMetaData() {
499 // metadata users are rare, so we lazily allocate it. If that changes we
500 // can decide to just make it a field in the device (rather than a ptr)
501 if (NULL == fMetaData) {
502 fMetaData = new SkMetaData;
503 }
504 return *fMetaData;
505}
506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507///////////////////////////////////////////////////////////////////////////////
508
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000509void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000510 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000511 if (device) {
512 device->flush();
513 }
514}
515
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000516SkISize SkCanvas::getTopLayerSize() const {
517 SkBaseDevice* d = this->getTopDevice();
518 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
519}
520
521SkIPoint SkCanvas::getTopLayerOrigin() const {
522 SkBaseDevice* d = this->getTopDevice();
523 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
524}
525
526SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000527 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000528 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
529}
530
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000531SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000533 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 SkASSERT(rec && rec->fLayer);
535 return rec->fLayer->fDevice;
536}
537
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000538SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000539 if (updateMatrixClip) {
540 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
541 }
reed@google.com9266fed2011-03-30 00:18:03 +0000542 return fMCRec->fTopLayer->fDevice;
543}
544
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000545SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000547 SkDeque::F2BIter iter(fMCStack);
548 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000550 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551
552 if (rootDevice == device) {
553 return device;
554 }
reed@google.com4b226022011-01-11 18:32:13 +0000555
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000557 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 }
559 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000560 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 }
562
563 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
564 rootDevice = device;
565
566 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000567
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 /* Now we update our initial region to have the bounds of the new device,
569 and then intersect all of the clips in our stack with these bounds,
570 to ensure that we can't draw outside of the device's bounds (and trash
571 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000572
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 NOTE: this is only a partial-fix, since if the new device is larger than
574 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000575 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
577 reconstruct the correct clips, so this approximation will have to do.
578 The caller really needs to restore() back to the base if they want to
579 accurately take advantage of the new device bounds.
580 */
581
reed@google.com42aea282012-03-28 16:19:15 +0000582 SkIRect bounds;
583 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000585 } else {
586 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 }
reed@google.com42aea282012-03-28 16:19:15 +0000588 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700589 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000590 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700591 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000592 }
593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 return device;
595}
596
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000597bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
598 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
599 return false;
600 }
601
602 bool weAllocated = false;
603 if (NULL == bitmap->pixelRef()) {
604 if (!bitmap->allocPixels()) {
605 return false;
606 }
607 weAllocated = true;
608 }
609
610 SkBitmap bm(*bitmap);
611 bm.lockPixels();
612 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
613 return true;
614 }
615
616 if (weAllocated) {
617 bitmap->setPixelRef(NULL);
618 }
619 return false;
620}
reed@google.com51df9e32010-12-23 19:29:18 +0000621
bsalomon@google.comc6980972011-11-02 19:57:21 +0000622bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000623 SkIRect r = srcRect;
624 const SkISize size = this->getBaseLayerSize();
625 if (!r.intersect(0, 0, size.width(), size.height())) {
626 bitmap->reset();
627 return false;
628 }
629
630 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
631 // bitmap will already be reset.
632 return false;
633 }
634 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
635 bitmap->reset();
636 return false;
637 }
638 return true;
639}
640
641bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
642 switch (origInfo.colorType()) {
643 case kUnknown_SkColorType:
644 case kIndex_8_SkColorType:
645 return false;
646 default:
647 break;
648 }
649 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
650 return false;
651 }
652 if (0 == origInfo.width() || 0 == origInfo.height()) {
653 return false;
654 }
655
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000656 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000657 if (!device) {
658 return false;
659 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000660
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000661 const SkISize size = this->getBaseLayerSize();
662 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
663 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000664 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000665 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000666
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000667 SkImageInfo info = origInfo;
668 // the intersect may have shrunk info's logical size
669 info.fWidth = srcR.width();
670 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000671
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000672 // if x or y are negative, then we have to adjust pixels
673 if (x > 0) {
674 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000675 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000676 if (y > 0) {
677 y = 0;
678 }
679 // here x,y are either 0 or negative
680 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000681
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000682 // The device can assert that the requested area is always contained in its bounds
683 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000684}
685
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000686bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
687 if (bitmap.getTexture()) {
688 return false;
689 }
690 SkBitmap bm(bitmap);
691 bm.lockPixels();
692 if (bm.getPixels()) {
693 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
694 }
695 return false;
696}
697
698bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
699 int x, int y) {
700 switch (origInfo.colorType()) {
701 case kUnknown_SkColorType:
702 case kIndex_8_SkColorType:
703 return false;
704 default:
705 break;
706 }
707 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
708 return false;
709 }
710
711 const SkISize size = this->getBaseLayerSize();
712 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
713 if (!target.intersect(0, 0, size.width(), size.height())) {
714 return false;
715 }
716
717 SkBaseDevice* device = this->getDevice();
718 if (!device) {
719 return false;
720 }
721
722 SkImageInfo info = origInfo;
723 // the intersect may have shrunk info's logical size
724 info.fWidth = target.width();
725 info.fHeight = target.height();
726
727 // if x or y are negative, then we have to adjust pixels
728 if (x > 0) {
729 x = 0;
730 }
731 if (y > 0) {
732 y = 0;
733 }
734 // here x,y are either 0 or negative
735 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
736
reed4af35f32014-06-27 17:47:49 -0700737 // Tell our owning surface to bump its generation ID
738 this->predrawNotify();
739
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000740 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000741 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000742}
reed@google.com51df9e32010-12-23 19:29:18 +0000743
junov@google.com4370aed2012-01-18 16:21:08 +0000744SkCanvas* SkCanvas::canvasForDrawIter() {
745 return this;
746}
747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748//////////////////////////////////////////////////////////////////////////////
749
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750void SkCanvas::updateDeviceCMCache() {
751 if (fDeviceCMDirty) {
752 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700753 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000755
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000757 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000759 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000761 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 } while ((layer = layer->fNext) != NULL);
763 }
764 fDeviceCMDirty = false;
765 }
766}
767
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768///////////////////////////////////////////////////////////////////////////////
769
Florin Malita5f6102d2014-06-30 10:13:28 -0400770int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000772
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400774 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000776
Florin Malita5f6102d2014-06-30 10:13:28 -0400777 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000778
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779 return saveCount;
780}
781
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000782int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400783 this->willSave();
784 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785}
786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000788#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000790#else
791 return true;
792#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793}
794
junov@chromium.orga907ac32012-02-24 21:54:07 +0000795bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000796 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000797 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000798 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000799 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000800 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000801 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000802
803 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700804 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000805 // Filters may grow the bounds beyond the device bounds.
806 op = SkRegion::kReplace_Op;
807 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000808 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 if (NULL != bounds) {
810 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000811
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 this->getTotalMatrix().mapRect(&r, *bounds);
813 r.roundOut(&ir);
814 // early exit if the layer's bounds are clipped out
815 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000816 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700817 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000818 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000819 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 }
821 } else { // no user bounds, so just use the clip
822 ir = clipBounds;
823 }
824
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000825 if (bounds_affects_clip(flags)) {
826 fClipStack.clipDevRect(ir, op);
827 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700828 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000829 return false;
830 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000831 }
832
833 if (intersection) {
834 *intersection = ir;
835 }
836 return true;
837}
838
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000839int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
840 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
841 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
842}
843
junov@chromium.orga907ac32012-02-24 21:54:07 +0000844int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
845 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000846 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
847 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000848}
849
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000850int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
851 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000852#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000853 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000854#endif
855
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856 // do this before we create the layer. We don't call the public save() since
857 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400858 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000859
860 fDeviceCMDirty = true;
861
862 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000863 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 return count;
865 }
866
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000867 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
868 // the clipRectBounds() call above?
869 if (kNoLayer_SaveLayerStrategy == strategy) {
870 return count;
871 }
872
reed@google.comb55deeb2012-01-06 14:43:09 +0000873 // Kill the imagefilter if our device doesn't allow it
874 SkLazyPaint lazyP;
875 if (paint && paint->getImageFilter()) {
876 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000877 if (justForImageFilter) {
878 // early exit if the layer was just for the imageFilter
879 return count;
880 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000881 SkPaint* p = lazyP.set(*paint);
882 p->setImageFilter(NULL);
883 paint = p;
884 }
885 }
886
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000887 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
888 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
889 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000891 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000892 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700893 device = this->getDevice();
894 if (device) {
895 device = device->createCompatibleDevice(info);
896 }
reed@google.com76dd2772012-01-05 21:15:07 +0000897 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000898 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000899 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000900 if (NULL == device) {
901 SkDebugf("Unable to create device for layer.");
902 return count;
903 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000904
reed@google.com6f8f2922011-03-04 22:27:10 +0000905 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000906 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 device->unref();
908
909 layer->fNext = fMCRec->fTopLayer;
910 fMCRec->fLayer = layer;
911 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
912
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000913 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 return count;
915}
916
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000917int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
918 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
919}
920
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
922 SaveFlags flags) {
923 if (0xFF == alpha) {
924 return this->saveLayer(bounds, NULL, flags);
925 } else {
926 SkPaint tmpPaint;
927 tmpPaint.setAlpha(alpha);
928 return this->saveLayer(bounds, &tmpPaint, flags);
929 }
930}
931
932void SkCanvas::restore() {
933 // check for underflow
934 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000935 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 this->internalRestore();
937 }
938}
939
940void SkCanvas::internalRestore() {
941 SkASSERT(fMCStack.count() != 0);
942
943 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000944 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945
Florin Malita5f6102d2014-06-30 10:13:28 -0400946 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000947
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000948 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 DeviceCM* layer = fMCRec->fLayer; // may be null
950 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
951 fMCRec->fLayer = NULL;
952
953 // now do the normal restore()
954 fMCRec->~MCRec(); // balanced in save()
955 fMCStack.pop_back();
956 fMCRec = (MCRec*)fMCStack.back();
957
958 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
959 since if we're being recorded, we don't want to record this (the
960 recorder will have already recorded the restore).
961 */
962 if (NULL != layer) {
963 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000964 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000965 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
966 layer->fPaint);
967 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000969
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000970 SkASSERT(fSaveLayerCount > 0);
971 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 }
973 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000974 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975}
976
977int SkCanvas::getSaveCount() const {
978 return fMCStack.count();
979}
980
981void SkCanvas::restoreToCount(int count) {
982 // sanity check
983 if (count < 1) {
984 count = 1;
985 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000986
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000987 int n = this->getSaveCount() - count;
988 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 this->restore();
990 }
991}
992
reed@google.com7c202932011-12-14 18:48:05 +0000993bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000994 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000995}
996
reed@google.com76f10a32014-02-05 15:32:21 +0000997SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
998 return this->onNewSurface(info);
999}
1000
1001SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1002 SkBaseDevice* dev = this->getDevice();
1003 return dev ? dev->newSurface(info) : NULL;
1004}
1005
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001006SkImageInfo SkCanvas::imageInfo() const {
1007 SkBaseDevice* dev = this->getDevice();
1008 if (dev) {
1009 return dev->imageInfo();
1010 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001011 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001012 }
1013}
1014
1015const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1016 return this->onPeekPixels(info, rowBytes);
1017}
1018
1019const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1020 SkBaseDevice* dev = this->getDevice();
1021 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1022}
1023
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001024void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1025 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1026 if (pixels && origin) {
1027 *origin = this->getTopDevice(false)->getOrigin();
1028 }
1029 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001030}
1031
1032void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1033 SkBaseDevice* dev = this->getTopDevice();
1034 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1035}
1036
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001037SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1038 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1039 if (NULL == fAddr) {
1040 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001041 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001042 return; // failure, fAddr is NULL
1043 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001044 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1045 return; // failure, fAddr is NULL
1046 }
1047 fAddr = fBitmap.getPixels();
1048 fRowBytes = fBitmap.rowBytes();
1049 }
1050 SkASSERT(fAddr); // success
1051}
1052
1053bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1054 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001055 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001056 } else {
1057 bitmap->reset();
1058 return false;
1059 }
1060}
1061
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001062void SkCanvas::onPushCull(const SkRect& cullRect) {
1063 // do nothing. Subclasses may do something
1064}
1065
1066void SkCanvas::onPopCull() {
1067 // do nothing. Subclasses may do something
1068}
1069
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001071#ifdef SK_DEBUG
1072// Ensure that cull rects are monotonically nested in device space.
1073void SkCanvas::validateCull(const SkIRect& devCull) {
1074 if (fCullStack.isEmpty()
1075 || devCull.isEmpty()
1076 || fCullStack.top().contains(devCull)) {
1077 return;
1078 }
1079
1080 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1081 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1082 fCullStack.top().x(), fCullStack.top().y(),
1083 fCullStack.top().right(), fCullStack.top().bottom()));
1084
1085#ifdef ASSERT_NESTED_CULLING
1086 SkDEBUGFAIL("Invalid cull.");
1087#endif
1088}
1089#endif
1090
1091void SkCanvas::pushCull(const SkRect& cullRect) {
1092 ++fCullCount;
1093 this->onPushCull(cullRect);
1094
1095#ifdef SK_DEBUG
1096 // Map the cull rect into device space.
1097 SkRect mappedCull;
1098 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1099
1100 // Take clipping into account.
1101 SkIRect devClip, devCull;
1102 mappedCull.roundOut(&devCull);
1103 this->getClipDeviceBounds(&devClip);
1104 if (!devCull.intersect(devClip)) {
1105 devCull.setEmpty();
1106 }
1107
1108 this->validateCull(devCull);
1109 fCullStack.push(devCull); // balanced in popCull
1110#endif
1111}
1112
1113void SkCanvas::popCull() {
1114 SkASSERT(fCullStack.count() == fCullCount);
1115
1116 if (fCullCount > 0) {
1117 --fCullCount;
1118 this->onPopCull();
1119
1120 SkDEBUGCODE(fCullStack.pop());
1121 }
1122}
1123
1124/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001126void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001128 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 return;
1130 }
1131
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001132 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001134 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001136
1137 SkDEBUGCODE(bitmap.validate();)
1138 CHECK_LOCKCOUNT_BALANCE(bitmap);
1139
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001140 SkRect storage;
1141 const SkRect* bounds = NULL;
1142 if (paint && paint->canComputeFastBounds()) {
1143 bitmap.getBounds(&storage);
1144 matrix.mapRect(&storage);
1145 bounds = &paint->computeFastBounds(storage, &storage);
1146 }
1147
1148 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001149
1150 while (iter.next()) {
1151 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1152 }
1153
1154 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155}
1156
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001157void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001158 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159 SkPaint tmp;
1160 if (NULL == paint) {
1161 tmp.setDither(true);
1162 paint = &tmp;
1163 }
reed@google.com4b226022011-01-11 18:32:13 +00001164
reed@google.com8926b162012-03-23 15:36:36 +00001165 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001167 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001168 paint = &looper.paint();
1169 SkImageFilter* filter = paint->getImageFilter();
1170 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001171 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001172 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001173 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001174 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001175 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001176 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001177 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001178 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001179 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1180 SkAutoUnref aur(NULL);
1181 if (!cache) {
1182 cache = SkImageFilter::Cache::Create();
1183 aur.reset(cache);
1184 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001185 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001186 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001187 SkPaint tmpUnfiltered(*paint);
1188 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001189 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1190 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001191 }
1192 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001193 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001194 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001196 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
reed@google.com8926b162012-03-23 15:36:36 +00001199void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1200 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001201 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001202 return;
1203 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001204 SkDEBUGCODE(bitmap.validate();)
1205 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001206
reed@google.com8926b162012-03-23 15:36:36 +00001207 SkPaint tmp;
1208 if (NULL == paint) {
1209 paint = &tmp;
1210 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001211
reed@google.com8926b162012-03-23 15:36:36 +00001212 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001213
reed@google.com8926b162012-03-23 15:36:36 +00001214 while (iter.next()) {
1215 paint = &looper.paint();
1216 SkImageFilter* filter = paint->getImageFilter();
1217 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1218 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001219 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001220 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001221 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001222 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001223 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001224 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001225 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1226 SkAutoUnref aur(NULL);
1227 if (!cache) {
1228 cache = SkImageFilter::Cache::Create();
1229 aur.reset(cache);
1230 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001231 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001232 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001233 SkPaint tmpUnfiltered(*paint);
1234 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001235 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001236 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001237 }
1238 } else {
1239 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1240 }
1241 }
1242 LOOPER_END
1243}
1244
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001246void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001247 SkMatrix m;
1248 m.setTranslate(dx, dy);
1249 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250}
1251
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001252void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001253 SkMatrix m;
1254 m.setScale(sx, sy);
1255 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256}
1257
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001258void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001259 SkMatrix m;
1260 m.setRotate(degrees);
1261 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262}
1263
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001264void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001265 SkMatrix m;
1266 m.setSkew(sx, sy);
1267 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001268}
1269
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001270void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001271 if (matrix.isIdentity()) {
1272 return;
1273 }
1274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001276 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001277 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001278
1279 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001280}
1281
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282void SkCanvas::setMatrix(const SkMatrix& matrix) {
1283 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001284 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001285 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001286 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287}
1288
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289void SkCanvas::resetMatrix() {
1290 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001291
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 matrix.reset();
1293 this->setMatrix(matrix);
1294}
1295
1296//////////////////////////////////////////////////////////////////////////////
1297
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001298void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001299 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1300 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001301}
1302
1303void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001304#ifdef SK_ENABLE_CLIP_QUICKREJECT
1305 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001306 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001307 return false;
1308 }
1309
reed@google.com3b3e8952012-08-16 20:53:31 +00001310 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001311 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001312 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001313
1314 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001315 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001316 }
1317 }
1318#endif
1319
reed@google.com5c3d1472011-02-22 19:12:23 +00001320 AutoValidateClip avc(this);
1321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001323 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001324 if (!fAllowSoftClip) {
1325 edgeStyle = kHard_ClipEdgeStyle;
1326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327
reed1f836ee2014-07-07 07:49:34 -07001328 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001329 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001330 // the matrix. This means we don't have to a) make a path, and b) tell
1331 // the region code to scan-convert the path, only to discover that it
1332 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334
reed1f836ee2014-07-07 07:49:34 -07001335 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001337 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001339 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001340 // and clip against that, since it can handle any matrix. However, to
1341 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1342 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 SkPath path;
1344
1345 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001346 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 }
1348}
1349
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001350static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1351 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001352 // base is used to limit the size (and therefore memory allocation) of the
1353 // region that results from scan converting devPath.
1354 SkRegion base;
1355
reed@google.com819c9212011-02-23 18:56:55 +00001356 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001357 // since we are intersect, we can do better (tighter) with currRgn's
1358 // bounds, than just using the device. However, if currRgn is complex,
1359 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001360 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001361 // FIXME: we should also be able to do this when currClip->isBW(),
1362 // but relaxing the test above triggers GM asserts in
1363 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001364 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001365 } else {
reed@google.com00177082011-10-12 14:34:30 +00001366 base.setRect(currClip->getBounds());
1367 SkRasterClip clip;
1368 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001369 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001370 }
reed@google.com819c9212011-02-23 18:56:55 +00001371 } else {
reed52d9ac62014-06-30 09:05:34 -07001372 const SkISize size = canvas->getBaseLayerSize();
1373 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001374
1375 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001376 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001377 } else {
reed@google.com00177082011-10-12 14:34:30 +00001378 SkRasterClip clip;
1379 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001381 }
1382 }
1383}
1384
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001385void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001386 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001387 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001388 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1389 } else {
1390 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001391 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001392}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001393
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001394void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001395 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001396 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001397 AutoValidateClip avc(this);
1398
1399 fDeviceCMDirty = true;
1400 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001401 if (!fAllowSoftClip) {
1402 edgeStyle = kHard_ClipEdgeStyle;
1403 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001404
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001405 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001406
1407 SkPath devPath;
1408 devPath.addRRect(transformedRRect);
1409
reed1f836ee2014-07-07 07:49:34 -07001410 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001411 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001412 }
1413
1414 SkPath path;
1415 path.addRRect(rrect);
1416 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001417 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001418}
1419
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001420void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1422 SkRect r;
1423 if (!path.isInverseFillType() && path.isRect(&r)) {
1424 this->onClipRect(r, op, edgeStyle);
1425 } else {
1426 this->onClipPath(path, op, edgeStyle);
1427 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428}
1429
1430void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001431#ifdef SK_ENABLE_CLIP_QUICKREJECT
1432 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001433 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001434 return false;
1435 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001436
reed@google.com3b3e8952012-08-16 20:53:31 +00001437 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001438 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001439 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001440
reed@google.comda17f752012-08-16 18:27:05 +00001441 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001442 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001443 }
1444 }
1445#endif
1446
reed@google.com5c3d1472011-02-22 19:12:23 +00001447 AutoValidateClip avc(this);
1448
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001450 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001451 if (!fAllowSoftClip) {
1452 edgeStyle = kHard_ClipEdgeStyle;
1453 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454
1455 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001456 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457
reed@google.comfe701122011-11-08 19:41:23 +00001458 // Check if the transfomation, or the original path itself
1459 // made us empty. Note this can also happen if we contained NaN
1460 // values. computing the bounds detects this, and will set our
1461 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1462 if (devPath.getBounds().isEmpty()) {
1463 // resetting the path will remove any NaN or other wanky values
1464 // that might upset our scan converter.
1465 devPath.reset();
1466 }
1467
reed@google.com5c3d1472011-02-22 19:12:23 +00001468 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001469 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001470
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001471 if (fAllowSimplifyClip) {
1472 devPath.reset();
1473 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1474 const SkClipStack* clipStack = getClipStack();
1475 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1476 const SkClipStack::Element* element;
1477 while ((element = iter.next())) {
1478 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001479 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001480 if (type != SkClipStack::Element::kEmpty_Type) {
1481 element->asPath(&operand);
1482 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001483 SkRegion::Op elementOp = element->getOp();
1484 if (elementOp == SkRegion::kReplace_Op) {
1485 devPath = operand;
1486 } else {
1487 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1488 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001489 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1490 // perhaps we need an API change to avoid this sort of mixed-signals about
1491 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001492 if (element->isAA()) {
1493 edgeStyle = kSoft_ClipEdgeStyle;
1494 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001495 }
1496 op = SkRegion::kReplace_Op;
1497 }
1498
reed1f836ee2014-07-07 07:49:34 -07001499 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500}
1501
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001502void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001503 bool inverseFilled) {
1504 // This is for updating the clip conservatively using only bounds
1505 // information.
1506 // Contract:
1507 // The current clip must contain the true clip. The true
1508 // clip is the clip that would have normally been computed
1509 // by calls to clipPath and clipRRect
1510 // Objective:
1511 // Keep the current clip as small as possible without
1512 // breaking the contract, using only clip bounding rectangles
1513 // (for performance).
1514
1515 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1516 // don't have to worry about getting caught in a loop. Thus anywhere
1517 // we call a virtual method, we explicitly prefix it with
1518 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001519
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001520 if (inverseFilled) {
1521 switch (op) {
1522 case SkRegion::kIntersect_Op:
1523 case SkRegion::kDifference_Op:
1524 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001525 // the clip unchanged conservatively respects the contract.
1526 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001527 case SkRegion::kUnion_Op:
1528 case SkRegion::kReplace_Op:
1529 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001530 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001531 // These ops can grow the current clip up to the extents of
1532 // the input clip, which is inverse filled, so we just set
1533 // the current clip to the device bounds.
1534 SkRect deviceBounds;
1535 SkIRect deviceIBounds;
1536 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001537 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001538
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001539 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001540 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001541 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001542 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001543 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001544 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001545 break;
1546 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001547 default:
1548 SkASSERT(0); // unhandled op?
1549 }
1550 } else {
1551 // Not inverse filled
1552 switch (op) {
1553 case SkRegion::kIntersect_Op:
1554 case SkRegion::kUnion_Op:
1555 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1557 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001558 case SkRegion::kDifference_Op:
1559 // Difference can only shrink the current clip.
1560 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001562 case SkRegion::kReverseDifference_Op:
1563 // To reverse, we swap in the bounds with a replace op.
1564 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1566 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001567 case SkRegion::kXOR_Op:
1568 // Be conservative, based on (A XOR B) always included in (A union B),
1569 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001570 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1571 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001572 default:
1573 SkASSERT(0); // unhandled op?
1574 }
1575 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001576}
1577
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001578void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001580}
1581
1582void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001583 AutoValidateClip avc(this);
1584
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001586 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587
reed@google.com5c3d1472011-02-22 19:12:23 +00001588 // todo: signal fClipStack that we have a region, and therefore (I guess)
1589 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001590 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001591
reed1f836ee2014-07-07 07:49:34 -07001592 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593}
1594
reed@google.com819c9212011-02-23 18:56:55 +00001595#ifdef SK_DEBUG
1596void SkCanvas::validateClip() const {
1597 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001598 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001599 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001600 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001601 return;
1602 }
1603
reed@google.com819c9212011-02-23 18:56:55 +00001604 SkIRect ir;
1605 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001606 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001607
robertphillips@google.com80214e22012-07-20 15:33:18 +00001608 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001609 const SkClipStack::Element* element;
1610 while ((element = iter.next()) != NULL) {
1611 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001612 case SkClipStack::Element::kRect_Type:
1613 element->getRect().round(&ir);
1614 tmpClip.op(ir, element->getOp());
1615 break;
1616 case SkClipStack::Element::kEmpty_Type:
1617 tmpClip.setEmpty();
1618 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001619 default: {
1620 SkPath path;
1621 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001623 break;
1624 }
reed@google.com819c9212011-02-23 18:56:55 +00001625 }
1626 }
reed@google.com819c9212011-02-23 18:56:55 +00001627}
1628#endif
1629
reed@google.com90c07ea2012-04-13 13:50:27 +00001630void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001631 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001632 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001633
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001634 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001635 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001636 }
1637}
1638
reed@google.com5c3d1472011-02-22 19:12:23 +00001639///////////////////////////////////////////////////////////////////////////////
1640
reed@google.com754de5f2014-02-24 19:38:20 +00001641bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001642 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001643}
1644
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001645bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001646 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001647}
1648
reed@google.com3b3e8952012-08-16 20:53:31 +00001649bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001650
reed@google.com16078632011-12-06 18:56:37 +00001651 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001652 return true;
1653
reed1f836ee2014-07-07 07:49:34 -07001654 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 return true;
1656 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657
reed1f836ee2014-07-07 07:49:34 -07001658 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001659 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001660 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001661 SkIRect idst;
1662 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001663 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001664 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001665 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001666
reed@android.coma380ae42009-07-21 01:17:02 +00001667 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001668 // TODO: should we use | instead, or compare all 4 at once?
1669 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001670 return true;
1671 }
reed@google.comc0784db2013-12-13 21:16:12 +00001672 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001673 return true;
1674 }
1675 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677}
1678
reed@google.com3b3e8952012-08-16 20:53:31 +00001679bool SkCanvas::quickReject(const SkPath& path) const {
1680 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681}
1682
reed@google.com3b3e8952012-08-16 20:53:31 +00001683bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001684 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001685 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 return false;
1687 }
1688
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001689 SkMatrix inverse;
1690 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001691 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001692 if (bounds) {
1693 bounds->setEmpty();
1694 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001695 return false;
1696 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001698 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001699 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001700 // adjust it outwards in case we are antialiasing
1701 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001702
reed@google.com8f4d2302013-12-17 16:44:46 +00001703 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1704 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705 inverse.mapRect(bounds, r);
1706 }
1707 return true;
1708}
1709
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001710bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001711 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001712 if (clip.isEmpty()) {
1713 if (bounds) {
1714 bounds->setEmpty();
1715 }
1716 return false;
1717 }
1718
1719 if (NULL != bounds) {
1720 *bounds = clip.getBounds();
1721 }
1722 return true;
1723}
1724
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001726 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727}
1728
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001729#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001730SkCanvas::ClipType SkCanvas::getClipType() const {
reed1f836ee2014-07-07 07:49:34 -07001731 if (fMCRec->fRasterClip.isEmpty()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001732 return kEmpty_ClipType;
1733 }
reed1f836ee2014-07-07 07:49:34 -07001734 if (fMCRec->fRasterClip.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001735 return kRect_ClipType;
1736 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001737 return kComplex_ClipType;
1738}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001739#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001740
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001741const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001742 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001743}
1744
1745void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1746 path->reset();
1747
reed1f836ee2014-07-07 07:49:34 -07001748 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001749 if (rgn.isEmpty()) {
1750 return;
1751 }
1752 (void)rgn.getBoundaryPath(path);
1753}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754
reed@google.com9c135db2014-03-12 18:28:35 +00001755GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1756 SkBaseDevice* dev = this->getTopDevice();
1757 return dev ? dev->accessRenderTarget() : NULL;
1758}
1759
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001760SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001761 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001762 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763}
1764
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001765GrContext* SkCanvas::getGrContext() {
1766#if SK_SUPPORT_GPU
1767 SkBaseDevice* device = this->getTopDevice();
1768 if (NULL != device) {
1769 GrRenderTarget* renderTarget = device->accessRenderTarget();
1770 if (NULL != renderTarget) {
1771 return renderTarget->getContext();
1772 }
1773 }
1774#endif
1775
1776 return NULL;
1777
1778}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001779
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001780void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1781 const SkPaint& paint) {
1782 if (outer.isEmpty()) {
1783 return;
1784 }
1785 if (inner.isEmpty()) {
1786 this->drawRRect(outer, paint);
1787 return;
1788 }
1789
1790 // We don't have this method (yet), but technically this is what we should
1791 // be able to assert...
1792 // SkASSERT(outer.contains(inner));
1793 //
1794 // For now at least check for containment of bounds
1795 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1796
1797 this->onDrawDRRect(outer, inner, paint);
1798}
1799
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800//////////////////////////////////////////////////////////////////////////////
1801// These are the virtual drawing methods
1802//////////////////////////////////////////////////////////////////////////////
1803
reed@google.com2a981812011-04-14 18:59:28 +00001804void SkCanvas::clear(SkColor color) {
1805 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001806 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001807 while (iter.next()) {
1808 iter.fDevice->clear(color);
1809 }
1810}
1811
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001812void SkCanvas::onDiscard() {
1813 if (NULL != fSurfaceBase) {
1814 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1815 }
1816}
1817
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001819 this->internalDrawPaint(paint);
1820}
1821
1822void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824
1825 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001826 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 }
1828
reed@google.com4e2b3d32011-04-07 14:18:59 +00001829 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830}
1831
1832void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1833 const SkPaint& paint) {
1834 if ((long)count <= 0) {
1835 return;
1836 }
1837
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001838 SkRect r, storage;
1839 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001840 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001841 // special-case 2 points (common for drawing a single line)
1842 if (2 == count) {
1843 r.set(pts[0], pts[1]);
1844 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001845 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001846 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001847 bounds = &paint.computeFastStrokeBounds(r, &storage);
1848 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001849 return;
1850 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001851 }
reed@google.coma584aed2012-05-16 14:06:02 +00001852
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853 SkASSERT(pts != NULL);
1854
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001855 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001856
reed@android.com8a1c16f2008-12-17 15:59:43 +00001857 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001858 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001859 }
reed@google.com4b226022011-01-11 18:32:13 +00001860
reed@google.com4e2b3d32011-04-07 14:18:59 +00001861 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862}
1863
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001864void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001865 SkRect storage;
1866 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001868 bounds = &paint.computeFastBounds(r, &storage);
1869 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870 return;
1871 }
1872 }
reed@google.com4b226022011-01-11 18:32:13 +00001873
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001874 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875
1876 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001877 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 }
1879
reed@google.com4e2b3d32011-04-07 14:18:59 +00001880 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881}
1882
reed@google.com4ed0fb72012-12-12 20:48:18 +00001883void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001884 SkRect storage;
1885 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001886 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001887 bounds = &paint.computeFastBounds(oval, &storage);
1888 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001889 return;
1890 }
1891 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001892
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001893 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001894
1895 while (iter.next()) {
1896 iter.fDevice->drawOval(iter, oval, looper.paint());
1897 }
1898
1899 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001900}
1901
1902void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001903 SkRect storage;
1904 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001905 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001906 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1907 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001908 return;
1909 }
1910 }
1911
1912 if (rrect.isRect()) {
1913 // call the non-virtual version
1914 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001915 return;
1916 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001917 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001918 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1919 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001920 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001921
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001922 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001923
1924 while (iter.next()) {
1925 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1926 }
1927
1928 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001929}
1930
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001931void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1932 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001933 SkRect storage;
1934 const SkRect* bounds = NULL;
1935 if (paint.canComputeFastBounds()) {
1936 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1937 if (this->quickReject(*bounds)) {
1938 return;
1939 }
1940 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001941
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001942 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001943
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001944 while (iter.next()) {
1945 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1946 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001947
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001948 LOOPER_END
1949}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001950
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001951void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001952 if (!path.isFinite()) {
1953 return;
1954 }
1955
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001956 SkRect storage;
1957 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001958 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001959 const SkRect& pathBounds = path.getBounds();
1960 bounds = &paint.computeFastBounds(pathBounds, &storage);
1961 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962 return;
1963 }
1964 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001965
1966 const SkRect& r = path.getBounds();
1967 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001968 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001969 this->internalDrawPaint(paint);
1970 }
1971 return;
1972 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001974 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975
1976 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001977 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 }
1979
reed@google.com4e2b3d32011-04-07 14:18:59 +00001980 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001981}
1982
1983void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1984 const SkPaint* paint) {
1985 SkDEBUGCODE(bitmap.validate();)
1986
reed@google.com3d608122011-11-21 15:16:16 +00001987 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001988 SkRect bounds = {
1989 x, y,
1990 x + SkIntToScalar(bitmap.width()),
1991 y + SkIntToScalar(bitmap.height())
1992 };
1993 if (paint) {
1994 (void)paint->computeFastBounds(bounds, &bounds);
1995 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001996 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997 return;
1998 }
1999 }
reed@google.com4b226022011-01-11 18:32:13 +00002000
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001 SkMatrix matrix;
2002 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002003 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004}
2005
reed@google.com9987ec32011-09-07 11:57:52 +00002006// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002007void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002008 const SkRect& dst, const SkPaint* paint,
2009 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002010 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011 return;
2012 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002013
reed@google.comea033602012-12-14 13:13:55 +00002014 CHECK_LOCKCOUNT_BALANCE(bitmap);
2015
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002016 SkRect storage;
2017 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002018 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002019 if (paint) {
2020 bounds = &paint->computeFastBounds(dst, &storage);
2021 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002022 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002023 return;
2024 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002025 }
reed@google.com3d608122011-11-21 15:16:16 +00002026
reed@google.com33535f32012-09-25 15:37:50 +00002027 SkLazyPaint lazy;
2028 if (NULL == paint) {
2029 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002031
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002032 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002033
reed@google.com33535f32012-09-25 15:37:50 +00002034 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002035 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002036 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002037
reed@google.com33535f32012-09-25 15:37:50 +00002038 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039}
2040
reed@google.com71121732012-09-18 15:14:33 +00002041void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002042 const SkRect& dst, const SkPaint* paint,
2043 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002044 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002045 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002046}
2047
reed@android.com8a1c16f2008-12-17 15:59:43 +00002048void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2049 const SkPaint* paint) {
2050 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002051 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002052}
2053
reed@google.com9987ec32011-09-07 11:57:52 +00002054void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2055 const SkIRect& center, const SkRect& dst,
2056 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002057 if (bitmap.drawsNothing()) {
2058 return;
2059 }
reed@google.com3d608122011-11-21 15:16:16 +00002060 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002061 SkRect storage;
2062 const SkRect* bounds = &dst;
2063 if (paint) {
2064 bounds = &paint->computeFastBounds(dst, &storage);
2065 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002066 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002067 return;
2068 }
2069 }
2070
reed@google.com9987ec32011-09-07 11:57:52 +00002071 const int32_t w = bitmap.width();
2072 const int32_t h = bitmap.height();
2073
2074 SkIRect c = center;
2075 // pin center to the bounds of the bitmap
2076 c.fLeft = SkMax32(0, center.fLeft);
2077 c.fTop = SkMax32(0, center.fTop);
2078 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2079 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2080
reed@google.com71121732012-09-18 15:14:33 +00002081 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002082 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002083 };
2084 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002085 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002086 };
reed@google.com9987ec32011-09-07 11:57:52 +00002087 SkScalar dstX[4] = {
2088 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2089 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2090 };
2091 SkScalar dstY[4] = {
2092 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2093 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2094 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002095
reed@google.com9987ec32011-09-07 11:57:52 +00002096 if (dstX[1] > dstX[2]) {
2097 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2098 dstX[2] = dstX[1];
2099 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002100
reed@google.com9987ec32011-09-07 11:57:52 +00002101 if (dstY[1] > dstY[2]) {
2102 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2103 dstY[2] = dstY[1];
2104 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002105
reed@google.com9987ec32011-09-07 11:57:52 +00002106 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002107 SkRect s, d;
2108
reed@google.com9987ec32011-09-07 11:57:52 +00002109 s.fTop = srcY[y];
2110 s.fBottom = srcY[y+1];
2111 d.fTop = dstY[y];
2112 d.fBottom = dstY[y+1];
2113 for (int x = 0; x < 3; x++) {
2114 s.fLeft = srcX[x];
2115 s.fRight = srcX[x+1];
2116 d.fLeft = dstX[x];
2117 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002118 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002119 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002120 }
2121 }
2122}
2123
2124void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2125 const SkRect& dst, const SkPaint* paint) {
2126 SkDEBUGCODE(bitmap.validate();)
2127
2128 // Need a device entry-point, so gpu can use a mesh
2129 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2130}
2131
reed@google.comf67e4cf2011-03-15 20:56:58 +00002132class SkDeviceFilteredPaint {
2133public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002134 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2135 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002136 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002137 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002138 newPaint->setFlags(flags.fFlags);
2139 newPaint->setHinting(flags.fHinting);
2140 fPaint = newPaint;
2141 } else {
2142 fPaint = &paint;
2143 }
2144 }
2145
reed@google.comf67e4cf2011-03-15 20:56:58 +00002146 const SkPaint& paint() const { return *fPaint; }
2147
2148private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002149 const SkPaint* fPaint;
2150 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002151};
2152
bungeman@google.com52c748b2011-08-22 21:30:43 +00002153void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2154 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002155 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002156 draw.fDevice->drawRect(draw, r, paint);
2157 } else {
2158 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002159 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002160 draw.fDevice->drawRect(draw, r, p);
2161 }
2162}
2163
2164void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2165 const char text[], size_t byteLength,
2166 SkScalar x, SkScalar y) {
2167 SkASSERT(byteLength == 0 || text != NULL);
2168
2169 // nothing to draw
2170 if (text == NULL || byteLength == 0 ||
2171 draw.fClip->isEmpty() ||
2172 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2173 return;
2174 }
2175
2176 SkScalar width = 0;
2177 SkPoint start;
2178
2179 start.set(0, 0); // to avoid warning
2180 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2181 SkPaint::kStrikeThruText_Flag)) {
2182 width = paint.measureText(text, byteLength);
2183
2184 SkScalar offsetX = 0;
2185 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2186 offsetX = SkScalarHalf(width);
2187 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2188 offsetX = width;
2189 }
2190 start.set(x - offsetX, y);
2191 }
2192
2193 if (0 == width) {
2194 return;
2195 }
2196
2197 uint32_t flags = paint.getFlags();
2198
2199 if (flags & (SkPaint::kUnderlineText_Flag |
2200 SkPaint::kStrikeThruText_Flag)) {
2201 SkScalar textSize = paint.getTextSize();
2202 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2203 SkRect r;
2204
2205 r.fLeft = start.fX;
2206 r.fRight = start.fX + width;
2207
2208 if (flags & SkPaint::kUnderlineText_Flag) {
2209 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2210 start.fY);
2211 r.fTop = offset;
2212 r.fBottom = offset + height;
2213 DrawRect(draw, paint, r, textSize);
2214 }
2215 if (flags & SkPaint::kStrikeThruText_Flag) {
2216 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2217 start.fY);
2218 r.fTop = offset;
2219 r.fBottom = offset + height;
2220 DrawRect(draw, paint, r, textSize);
2221 }
2222 }
2223}
2224
reed@google.come0d9ce82014-04-23 04:00:17 +00002225void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2226 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002227 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002228
2229 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002230 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002231 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002232 DrawTextDecorations(iter, dfp.paint(),
2233 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002234 }
2235
reed@google.com4e2b3d32011-04-07 14:18:59 +00002236 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237}
2238
reed@google.come0d9ce82014-04-23 04:00:17 +00002239void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2240 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002241 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002242
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002244 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002246 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002248
reed@google.com4e2b3d32011-04-07 14:18:59 +00002249 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250}
2251
reed@google.come0d9ce82014-04-23 04:00:17 +00002252void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2253 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002254 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002255
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002257 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002259 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002261
reed@google.com4e2b3d32011-04-07 14:18:59 +00002262 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263}
2264
reed@google.come0d9ce82014-04-23 04:00:17 +00002265void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2266 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002267 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002268
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 while (iter.next()) {
2270 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002271 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002273
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002274 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002275}
2276
reed@google.come0d9ce82014-04-23 04:00:17 +00002277// These will become non-virtual, so they always call the (virtual) onDraw... method
2278void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2279 const SkPaint& paint) {
2280 this->onDrawText(text, byteLength, x, y, paint);
2281}
2282void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2283 const SkPaint& paint) {
2284 this->onDrawPosText(text, byteLength, pos, paint);
2285}
2286void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2287 SkScalar constY, const SkPaint& paint) {
2288 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2289}
2290void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2291 const SkMatrix* matrix, const SkPaint& paint) {
2292 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2293}
2294
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2296 const SkPoint verts[], const SkPoint texs[],
2297 const SkColor colors[], SkXfermode* xmode,
2298 const uint16_t indices[], int indexCount,
2299 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002300 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002301
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 while (iter.next()) {
2303 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002304 colors, xmode, indices, indexCount,
2305 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 }
reed@google.com4b226022011-01-11 18:32:13 +00002307
reed@google.com4e2b3d32011-04-07 14:18:59 +00002308 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309}
2310
2311//////////////////////////////////////////////////////////////////////////////
2312// These methods are NOT virtual, and therefore must call back into virtual
2313// methods, rather than actually drawing themselves.
2314//////////////////////////////////////////////////////////////////////////////
2315
2316void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002317 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002318 SkPaint paint;
2319
2320 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002321 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002322 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002323 }
2324 this->drawPaint(paint);
2325}
2326
reed@android.com845fdac2009-06-23 03:01:32 +00002327void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328 SkPaint paint;
2329
2330 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002331 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002332 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 }
2334 this->drawPaint(paint);
2335}
2336
2337void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2338 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002339
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 pt.set(x, y);
2341 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2342}
2343
2344void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2345 SkPoint pt;
2346 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002347
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348 pt.set(x, y);
2349 paint.setColor(color);
2350 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2351}
2352
2353void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2354 const SkPaint& paint) {
2355 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002356
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 pts[0].set(x0, y0);
2358 pts[1].set(x1, y1);
2359 this->drawPoints(kLines_PointMode, 2, pts, paint);
2360}
2361
2362void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2363 SkScalar right, SkScalar bottom,
2364 const SkPaint& paint) {
2365 SkRect r;
2366
2367 r.set(left, top, right, bottom);
2368 this->drawRect(r, paint);
2369}
2370
2371void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2372 const SkPaint& paint) {
2373 if (radius < 0) {
2374 radius = 0;
2375 }
2376
2377 SkRect r;
2378 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002379 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380}
2381
2382void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2383 const SkPaint& paint) {
2384 if (rx > 0 && ry > 0) {
2385 if (paint.canComputeFastBounds()) {
2386 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002387 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 return;
2389 }
2390 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002391 SkRRect rrect;
2392 rrect.setRectXY(r, rx, ry);
2393 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 } else {
2395 this->drawRect(r, paint);
2396 }
2397}
2398
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2400 SkScalar sweepAngle, bool useCenter,
2401 const SkPaint& paint) {
2402 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2403 this->drawOval(oval, paint);
2404 } else {
2405 SkPath path;
2406 if (useCenter) {
2407 path.moveTo(oval.centerX(), oval.centerY());
2408 }
2409 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2410 if (useCenter) {
2411 path.close();
2412 }
2413 this->drawPath(path, paint);
2414 }
2415}
2416
2417void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2418 const SkPath& path, SkScalar hOffset,
2419 SkScalar vOffset, const SkPaint& paint) {
2420 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002421
reed@android.com8a1c16f2008-12-17 15:59:43 +00002422 matrix.setTranslate(hOffset, vOffset);
2423 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2424}
2425
reed@android.comf76bacf2009-05-13 14:00:33 +00002426///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002427void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002428 SkBaseDevice* device = this->getDevice();
2429 if (NULL != device) {
2430 device->EXPERIMENTAL_optimize(picture);
2431 }
2432}
reed@android.comf76bacf2009-05-13 14:00:33 +00002433
robertphillips9b14f262014-06-04 05:40:44 -07002434void SkCanvas::EXPERIMENTAL_purge(const SkPicture* picture) {
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002435 SkBaseDevice* device = this->getTopDevice();
2436 if (NULL != device) {
2437 device->EXPERIMENTAL_purge(picture);
2438 }
2439}
2440
robertphillips9b14f262014-06-04 05:40:44 -07002441void SkCanvas::drawPicture(const SkPicture* picture) {
2442 if (NULL != picture) {
2443 this->onDrawPicture(picture);
2444 }
2445}
2446
2447void SkCanvas::onDrawPicture(const SkPicture* picture) {
2448 SkASSERT(NULL != picture);
2449
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002450 SkBaseDevice* device = this->getTopDevice();
2451 if (NULL != device) {
2452 // Canvas has to first give the device the opportunity to render
2453 // the picture itself.
robertphillips9b14f262014-06-04 05:40:44 -07002454 if (device->EXPERIMENTAL_drawPicture(this, picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002455 return; // the device has rendered the entire picture
2456 }
2457 }
2458
robertphillips9b14f262014-06-04 05:40:44 -07002459 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460}
2461
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462///////////////////////////////////////////////////////////////////////////////
2463///////////////////////////////////////////////////////////////////////////////
2464
2465SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002466 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002467
2468 SkASSERT(canvas);
2469
2470 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2471 fDone = !fImpl->next();
2472}
2473
2474SkCanvas::LayerIter::~LayerIter() {
2475 fImpl->~SkDrawIter();
2476}
2477
2478void SkCanvas::LayerIter::next() {
2479 fDone = !fImpl->next();
2480}
2481
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002482SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002483 return fImpl->getDevice();
2484}
2485
2486const SkMatrix& SkCanvas::LayerIter::matrix() const {
2487 return fImpl->getMatrix();
2488}
2489
2490const SkPaint& SkCanvas::LayerIter::paint() const {
2491 const SkPaint* paint = fImpl->getPaint();
2492 if (NULL == paint) {
2493 paint = &fDefaultPaint;
2494 }
2495 return *paint;
2496}
2497
2498const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2499int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2500int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002501
2502///////////////////////////////////////////////////////////////////////////////
2503
fmalitac3b589a2014-06-05 12:40:07 -07002504SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002505
2506///////////////////////////////////////////////////////////////////////////////
2507
2508static bool supported_for_raster_canvas(const SkImageInfo& info) {
2509 switch (info.alphaType()) {
2510 case kPremul_SkAlphaType:
2511 case kOpaque_SkAlphaType:
2512 break;
2513 default:
2514 return false;
2515 }
2516
2517 switch (info.colorType()) {
2518 case kAlpha_8_SkColorType:
2519 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002520 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002521 break;
2522 default:
2523 return false;
2524 }
2525
2526 return true;
2527}
2528
2529SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2530 if (!supported_for_raster_canvas(info)) {
2531 return NULL;
2532 }
2533
2534 SkBitmap bitmap;
2535 if (!bitmap.allocPixels(info)) {
2536 return NULL;
2537 }
2538
2539 // should this functionality be moved into allocPixels()?
2540 if (!bitmap.info().isOpaque()) {
2541 bitmap.eraseColor(0);
2542 }
2543 return SkNEW_ARGS(SkCanvas, (bitmap));
2544}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002545
2546SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2547 if (!supported_for_raster_canvas(info)) {
2548 return NULL;
2549 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002550
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002551 SkBitmap bitmap;
2552 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2553 return NULL;
2554 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002555 return SkNEW_ARGS(SkCanvas, (bitmap));
2556}