blob: 2fb5c951249d54c1905107e0a1c6161e1461281a [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:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000192 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000193 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
194 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
195 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000196
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 DeviceCM* fLayer;
198 /* If there are any layers in the stack, this points to the top-most
199 one that is at or below this level in the stack (so we know what
200 bitmap/device to draw into from this level. This value is NOT
201 reference counted, since the real owner is either our fLayer field,
202 or a previous one in a lower level.)
203 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000204 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000206 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 if (NULL != prev) {
208 if (flags & SkCanvas::kMatrix_SaveFlag) {
209 fMatrixStorage = *prev->fMatrix;
210 fMatrix = &fMatrixStorage;
211 } else {
212 fMatrix = prev->fMatrix;
213 }
reed@google.com4b226022011-01-11 18:32:13 +0000214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000216 fRasterClipStorage = *prev->fRasterClip;
217 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 } else {
reed@google.com00177082011-10-12 14:34:30 +0000219 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 }
221
222 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000223 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224
225 fTopLayer = prev->fTopLayer;
226 } else { // no prev
227 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000228
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000230 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 fFilter = NULL;
232 fTopLayer = NULL;
233 }
234 fLayer = NULL;
235
236 // don't bother initializing fNext
237 inc_rec();
238 }
239 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000240 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 SkDELETE(fLayer);
242 dec_rec();
243 }
reed@google.com4b226022011-01-11 18:32:13 +0000244
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245private:
reed@google.com00177082011-10-12 14:34:30 +0000246 SkMatrix fMatrixStorage;
247 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248};
249
250class SkDrawIter : public SkDraw {
251public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000252 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000253 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000254 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 canvas->updateDeviceCMCache();
256
reed@google.com90c07ea2012-04-13 13:50:27 +0000257 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000259 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 }
reed@google.com4b226022011-01-11 18:32:13 +0000261
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 bool next() {
263 // skip over recs with empty clips
264 if (fSkipEmptyClips) {
265 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
266 fCurrLayer = fCurrLayer->fNext;
267 }
268 }
269
reed@google.comf68c5e22012-02-24 16:38:58 +0000270 const DeviceCM* rec = fCurrLayer;
271 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272
273 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000274 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
275 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 fDevice = rec->fDevice;
277 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000279 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280
281 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000283
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 return true;
285 }
286 return false;
287 }
reed@google.com4b226022011-01-11 18:32:13 +0000288
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000289 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000290 int getX() const { return fDevice->getOrigin().x(); }
291 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 const SkMatrix& getMatrix() const { return *fMatrix; }
293 const SkRegion& getClip() const { return *fClip; }
294 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000295
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296private:
297 SkCanvas* fCanvas;
298 const DeviceCM* fCurrLayer;
299 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 SkBool8 fSkipEmptyClips;
301
302 typedef SkDraw INHERITED;
303};
304
305/////////////////////////////////////////////////////////////////////////////
306
307class AutoDrawLooper {
308public:
reed@google.com8926b162012-03-23 15:36:36 +0000309 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000310 bool skipLayerForImageFilter = false,
311 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000312 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000314 fPaint = NULL;
315 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000316 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000317 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318
reed@google.com8926b162012-03-23 15:36:36 +0000319 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
320 SkPaint tmp;
321 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000322 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
323 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000324 // we'll clear the imageFilter for the actual draws in next(), so
325 // it will only be applied during the restore().
326 fDoClearImageFilter = true;
327 }
328
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000329 if (SkDrawLooper* looper = paint.getLooper()) {
330 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
331 looper->contextSize());
332 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000333 fIsSimple = false;
334 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000335 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000336 // can we be marked as simple?
337 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000338 }
339 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000342 if (fDoClearImageFilter) {
343 fCanvas->internalRestore();
344 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000347
reed@google.com4e2b3d32011-04-07 14:18:59 +0000348 const SkPaint& paint() const {
349 SkASSERT(fPaint);
350 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000352
reed@google.com129ec222012-05-15 13:24:09 +0000353 bool next(SkDrawFilter::Type drawType) {
354 if (fDone) {
355 return false;
356 } else if (fIsSimple) {
357 fDone = true;
358 fPaint = &fOrigPaint;
359 return !fPaint->nothingToDraw();
360 } else {
361 return this->doNext(drawType);
362 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000363 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000364
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000366 SkLazyPaint fLazyPaint;
367 SkCanvas* fCanvas;
368 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000369 SkDrawFilter* fFilter;
370 const SkPaint* fPaint;
371 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000372 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000373 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000374 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000375 SkDrawLooper::Context* fLooperContext;
376 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000377
378 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379};
380
reed@google.com129ec222012-05-15 13:24:09 +0000381bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000382 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000383 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000384 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000385
386 SkPaint* paint = fLazyPaint.set(fOrigPaint);
387
388 if (fDoClearImageFilter) {
389 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000391
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000392 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000393 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000394 return false;
395 }
396 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000397 if (!fFilter->filter(paint, drawType)) {
398 fDone = true;
399 return false;
400 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000401 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000402 // no looper means we only draw once
403 fDone = true;
404 }
405 }
406 fPaint = paint;
407
408 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000409 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000410 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000411 }
412
413 // call this after any possible paint modifiers
414 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 fPaint = NULL;
416 return false;
417 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 return true;
419}
420
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421#include "SkColorPriv.h"
422
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423////////// macros to place around the internal draw calls //////////////////
424
reed@google.com8926b162012-03-23 15:36:36 +0000425#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000426 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000427 AutoDrawLooper looper(this, paint, true); \
428 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000429 SkDrawIter iter(this);
430
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000431#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000432 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000433 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000436
reed@google.com4e2b3d32011-04-07 14:18:59 +0000437#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438
439////////////////////////////////////////////////////////////////////////////
440
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000441SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000442 fCachedLocalClipBounds.setEmpty();
443 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000444 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000445 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000446 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000447 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000448 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000449 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450
451 fMCRec = (MCRec*)fMCStack.push_back();
452 new (fMCRec) MCRec(NULL, 0);
453
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000454 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456
reed@google.com97af1a62012-08-28 12:19:02 +0000457 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000458
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000459 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460}
461
reed@google.comcde92112011-07-06 20:00:52 +0000462SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000463 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
464{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000465 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000466
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000467 this->init(NULL);
468}
469
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000470SkCanvas::SkCanvas(int width, int height)
471 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
472{
473 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000474
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000475 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000476 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000477 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
478}
479
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000480SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000481 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
482{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 inc_canvas();
484
485 this->init(device);
486}
487
488SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000489 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
490{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 inc_canvas();
492
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000493 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494}
495
496SkCanvas::~SkCanvas() {
497 // free up the contents of our deque
498 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000499 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000500
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 this->internalRestore(); // restore the last, since we're going away
502
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000503 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 dec_canvas();
506}
507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508SkDrawFilter* SkCanvas::getDrawFilter() const {
509 return fMCRec->fFilter;
510}
511
512SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
513 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
514 return filter;
515}
516
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000517SkMetaData& SkCanvas::getMetaData() {
518 // metadata users are rare, so we lazily allocate it. If that changes we
519 // can decide to just make it a field in the device (rather than a ptr)
520 if (NULL == fMetaData) {
521 fMetaData = new SkMetaData;
522 }
523 return *fMetaData;
524}
525
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526///////////////////////////////////////////////////////////////////////////////
527
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000528void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000529 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000530 if (device) {
531 device->flush();
532 }
533}
534
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000535SkISize SkCanvas::getTopLayerSize() const {
536 SkBaseDevice* d = this->getTopDevice();
537 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
538}
539
540SkIPoint SkCanvas::getTopLayerOrigin() const {
541 SkBaseDevice* d = this->getTopDevice();
542 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
543}
544
545SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000546 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000547 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
548}
549
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000550SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000552 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 SkASSERT(rec && rec->fLayer);
554 return rec->fLayer->fDevice;
555}
556
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000557SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000558 if (updateMatrixClip) {
559 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
560 }
reed@google.com9266fed2011-03-30 00:18:03 +0000561 return fMCRec->fTopLayer->fDevice;
562}
563
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000564SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000566 SkDeque::F2BIter iter(fMCStack);
567 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000569 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570
571 if (rootDevice == device) {
572 return device;
573 }
reed@google.com4b226022011-01-11 18:32:13 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000576 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 }
578 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000579 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 }
581
582 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
583 rootDevice = device;
584
585 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000586
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 /* Now we update our initial region to have the bounds of the new device,
588 and then intersect all of the clips in our stack with these bounds,
589 to ensure that we can't draw outside of the device's bounds (and trash
590 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 NOTE: this is only a partial-fix, since if the new device is larger than
593 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000594 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
596 reconstruct the correct clips, so this approximation will have to do.
597 The caller really needs to restore() back to the base if they want to
598 accurately take advantage of the new device bounds.
599 */
600
reed@google.com42aea282012-03-28 16:19:15 +0000601 SkIRect bounds;
602 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000604 } else {
605 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 }
reed@google.com42aea282012-03-28 16:19:15 +0000607 // now jam our 1st clip to be bounds, and intersect the rest with that
608 rec->fRasterClip->setRect(bounds);
609 while ((rec = (MCRec*)iter.next()) != NULL) {
610 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
611 }
612
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 return device;
614}
615
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000616bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
617 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
618 return false;
619 }
620
621 bool weAllocated = false;
622 if (NULL == bitmap->pixelRef()) {
623 if (!bitmap->allocPixels()) {
624 return false;
625 }
626 weAllocated = true;
627 }
628
629 SkBitmap bm(*bitmap);
630 bm.lockPixels();
631 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
632 return true;
633 }
634
635 if (weAllocated) {
636 bitmap->setPixelRef(NULL);
637 }
638 return false;
639}
reed@google.com51df9e32010-12-23 19:29:18 +0000640
bsalomon@google.comc6980972011-11-02 19:57:21 +0000641bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000642 SkIRect r = srcRect;
643 const SkISize size = this->getBaseLayerSize();
644 if (!r.intersect(0, 0, size.width(), size.height())) {
645 bitmap->reset();
646 return false;
647 }
648
649 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
650 // bitmap will already be reset.
651 return false;
652 }
653 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
654 bitmap->reset();
655 return false;
656 }
657 return true;
658}
659
660bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
661 switch (origInfo.colorType()) {
662 case kUnknown_SkColorType:
663 case kIndex_8_SkColorType:
664 return false;
665 default:
666 break;
667 }
668 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
669 return false;
670 }
671 if (0 == origInfo.width() || 0 == origInfo.height()) {
672 return false;
673 }
674
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000675 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000676 if (!device) {
677 return false;
678 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000679
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000680 const SkISize size = this->getBaseLayerSize();
681 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
682 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000683 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000684 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000685
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000686 SkImageInfo info = origInfo;
687 // the intersect may have shrunk info's logical size
688 info.fWidth = srcR.width();
689 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000690
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000691 // if x or y are negative, then we have to adjust pixels
692 if (x > 0) {
693 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000694 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000695 if (y > 0) {
696 y = 0;
697 }
698 // here x,y are either 0 or negative
699 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000700
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000701 // The device can assert that the requested area is always contained in its bounds
702 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000703}
704
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000705bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
706 if (bitmap.getTexture()) {
707 return false;
708 }
709 SkBitmap bm(bitmap);
710 bm.lockPixels();
711 if (bm.getPixels()) {
712 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
713 }
714 return false;
715}
716
717bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
718 int x, int y) {
719 switch (origInfo.colorType()) {
720 case kUnknown_SkColorType:
721 case kIndex_8_SkColorType:
722 return false;
723 default:
724 break;
725 }
726 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
727 return false;
728 }
729
730 const SkISize size = this->getBaseLayerSize();
731 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
732 if (!target.intersect(0, 0, size.width(), size.height())) {
733 return false;
734 }
735
736 SkBaseDevice* device = this->getDevice();
737 if (!device) {
738 return false;
739 }
740
741 SkImageInfo info = origInfo;
742 // the intersect may have shrunk info's logical size
743 info.fWidth = target.width();
744 info.fHeight = target.height();
745
746 // if x or y are negative, then we have to adjust pixels
747 if (x > 0) {
748 x = 0;
749 }
750 if (y > 0) {
751 y = 0;
752 }
753 // here x,y are either 0 or negative
754 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
755
reed4af35f32014-06-27 17:47:49 -0700756 // Tell our owning surface to bump its generation ID
757 this->predrawNotify();
758
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000759 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000760 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000761}
reed@google.com51df9e32010-12-23 19:29:18 +0000762
junov@google.com4370aed2012-01-18 16:21:08 +0000763SkCanvas* SkCanvas::canvasForDrawIter() {
764 return this;
765}
766
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767//////////////////////////////////////////////////////////////////////////////
768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769void SkCanvas::updateDeviceCMCache() {
770 if (fDeviceCMDirty) {
771 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000772 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000774
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000776 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000778 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000780 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 } while ((layer = layer->fNext) != NULL);
782 }
783 fDeviceCMDirty = false;
784 }
785}
786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787///////////////////////////////////////////////////////////////////////////////
788
789int SkCanvas::internalSave(SaveFlags flags) {
790 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 MCRec* newTop = (MCRec*)fMCStack.push_back();
793 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000796
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000797 if (SkCanvas::kClip_SaveFlag & flags) {
798 fClipStack.save();
799 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000800
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 return saveCount;
802}
803
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000804int SkCanvas::save() {
805 this->willSave(kMatrixClip_SaveFlag);
806 return this->internalSave(kMatrixClip_SaveFlag);
807}
808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000810 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 // call shared impl
812 return this->internalSave(flags);
813}
814
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000816#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000818#else
819 return true;
820#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821}
822
junov@chromium.orga907ac32012-02-24 21:54:07 +0000823bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000824 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000825 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000826 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000827 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000828 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000829 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000830
831 if (imageFilter) {
832 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
833 // Filters may grow the bounds beyond the device bounds.
834 op = SkRegion::kReplace_Op;
835 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000836 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 if (NULL != bounds) {
838 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000839
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 this->getTotalMatrix().mapRect(&r, *bounds);
841 r.roundOut(&ir);
842 // early exit if the layer's bounds are clipped out
843 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000844 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000845 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000846 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000847 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 }
849 } else { // no user bounds, so just use the clip
850 ir = clipBounds;
851 }
852
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000853 if (bounds_affects_clip(flags)) {
854 fClipStack.clipDevRect(ir, op);
855 // early exit if the clip is now empty
856 if (!fMCRec->fRasterClip->op(ir, op)) {
857 return false;
858 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000859 }
860
861 if (intersection) {
862 *intersection = ir;
863 }
864 return true;
865}
866
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000867int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
868 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
869 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
870}
871
junov@chromium.orga907ac32012-02-24 21:54:07 +0000872int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
873 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000874 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
875 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000876}
877
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000878static SkBaseDevice* create_compatible_device(SkCanvas* canvas,
879 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000880 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000881 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000882}
883
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000884int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
885 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000886#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000887 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000888#endif
889
junov@chromium.orga907ac32012-02-24 21:54:07 +0000890 // do this before we create the layer. We don't call the public save() since
891 // that would invoke a possibly overridden virtual
892 int count = this->internalSave(flags);
893
894 fDeviceCMDirty = true;
895
896 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000897 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 return count;
899 }
900
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000901 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
902 // the clipRectBounds() call above?
903 if (kNoLayer_SaveLayerStrategy == strategy) {
904 return count;
905 }
906
reed@google.comb55deeb2012-01-06 14:43:09 +0000907 // Kill the imagefilter if our device doesn't allow it
908 SkLazyPaint lazyP;
909 if (paint && paint->getImageFilter()) {
910 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000911 if (justForImageFilter) {
912 // early exit if the layer was just for the imageFilter
913 return count;
914 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000915 SkPaint* p = lazyP.set(*paint);
916 p->setImageFilter(NULL);
917 paint = p;
918 }
919 }
920
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000921 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
922 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
923 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000925 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000926 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000927 device = create_compatible_device(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000928 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000929 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000930 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000931 if (NULL == device) {
932 SkDebugf("Unable to create device for layer.");
933 return count;
934 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000935
reed@google.com6f8f2922011-03-04 22:27:10 +0000936 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000937 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 device->unref();
939
940 layer->fNext = fMCRec->fTopLayer;
941 fMCRec->fLayer = layer;
942 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
943
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000944 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945 return count;
946}
947
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000948int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
949 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
950}
951
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
953 SaveFlags flags) {
954 if (0xFF == alpha) {
955 return this->saveLayer(bounds, NULL, flags);
956 } else {
957 SkPaint tmpPaint;
958 tmpPaint.setAlpha(alpha);
959 return this->saveLayer(bounds, &tmpPaint, flags);
960 }
961}
962
963void SkCanvas::restore() {
964 // check for underflow
965 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000966 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 this->internalRestore();
968 }
969}
970
971void SkCanvas::internalRestore() {
972 SkASSERT(fMCStack.count() != 0);
973
974 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000975 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000977 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
978 fClipStack.restore();
979 }
980
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000981 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 DeviceCM* layer = fMCRec->fLayer; // may be null
983 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
984 fMCRec->fLayer = NULL;
985
986 // now do the normal restore()
987 fMCRec->~MCRec(); // balanced in save()
988 fMCStack.pop_back();
989 fMCRec = (MCRec*)fMCStack.back();
990
991 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
992 since if we're being recorded, we don't want to record this (the
993 recorder will have already recorded the restore).
994 */
995 if (NULL != layer) {
996 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000997 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000998 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
999 layer->fPaint);
1000 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001002
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001003 SkASSERT(fSaveLayerCount > 0);
1004 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 }
1006 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001007 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008}
1009
1010int SkCanvas::getSaveCount() const {
1011 return fMCStack.count();
1012}
1013
1014void SkCanvas::restoreToCount(int count) {
1015 // sanity check
1016 if (count < 1) {
1017 count = 1;
1018 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001019
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001020 int n = this->getSaveCount() - count;
1021 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 this->restore();
1023 }
1024}
1025
reed@google.com7c202932011-12-14 18:48:05 +00001026bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001027 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001028}
1029
reed@google.com76f10a32014-02-05 15:32:21 +00001030SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1031 return this->onNewSurface(info);
1032}
1033
1034SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1035 SkBaseDevice* dev = this->getDevice();
1036 return dev ? dev->newSurface(info) : NULL;
1037}
1038
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001039SkImageInfo SkCanvas::imageInfo() const {
1040 SkBaseDevice* dev = this->getDevice();
1041 if (dev) {
1042 return dev->imageInfo();
1043 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001044 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001045 }
1046}
1047
1048const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1049 return this->onPeekPixels(info, rowBytes);
1050}
1051
1052const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1053 SkBaseDevice* dev = this->getDevice();
1054 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1055}
1056
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001057void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1058 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1059 if (pixels && origin) {
1060 *origin = this->getTopDevice(false)->getOrigin();
1061 }
1062 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001063}
1064
1065void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1066 SkBaseDevice* dev = this->getTopDevice();
1067 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1068}
1069
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001070SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1071 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1072 if (NULL == fAddr) {
1073 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001074 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001075 return; // failure, fAddr is NULL
1076 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001077 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1078 return; // failure, fAddr is NULL
1079 }
1080 fAddr = fBitmap.getPixels();
1081 fRowBytes = fBitmap.rowBytes();
1082 }
1083 SkASSERT(fAddr); // success
1084}
1085
1086bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1087 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001088 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001089 } else {
1090 bitmap->reset();
1091 return false;
1092 }
1093}
1094
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001095void SkCanvas::onPushCull(const SkRect& cullRect) {
1096 // do nothing. Subclasses may do something
1097}
1098
1099void SkCanvas::onPopCull() {
1100 // do nothing. Subclasses may do something
1101}
1102
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001104#ifdef SK_DEBUG
1105// Ensure that cull rects are monotonically nested in device space.
1106void SkCanvas::validateCull(const SkIRect& devCull) {
1107 if (fCullStack.isEmpty()
1108 || devCull.isEmpty()
1109 || fCullStack.top().contains(devCull)) {
1110 return;
1111 }
1112
1113 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1114 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1115 fCullStack.top().x(), fCullStack.top().y(),
1116 fCullStack.top().right(), fCullStack.top().bottom()));
1117
1118#ifdef ASSERT_NESTED_CULLING
1119 SkDEBUGFAIL("Invalid cull.");
1120#endif
1121}
1122#endif
1123
1124void SkCanvas::pushCull(const SkRect& cullRect) {
1125 ++fCullCount;
1126 this->onPushCull(cullRect);
1127
1128#ifdef SK_DEBUG
1129 // Map the cull rect into device space.
1130 SkRect mappedCull;
1131 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1132
1133 // Take clipping into account.
1134 SkIRect devClip, devCull;
1135 mappedCull.roundOut(&devCull);
1136 this->getClipDeviceBounds(&devClip);
1137 if (!devCull.intersect(devClip)) {
1138 devCull.setEmpty();
1139 }
1140
1141 this->validateCull(devCull);
1142 fCullStack.push(devCull); // balanced in popCull
1143#endif
1144}
1145
1146void SkCanvas::popCull() {
1147 SkASSERT(fCullStack.count() == fCullCount);
1148
1149 if (fCullCount > 0) {
1150 --fCullCount;
1151 this->onPopCull();
1152
1153 SkDEBUGCODE(fCullStack.pop());
1154 }
1155}
1156
1157/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001159void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001161 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 return;
1163 }
1164
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001165 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001167 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001169
1170 SkDEBUGCODE(bitmap.validate();)
1171 CHECK_LOCKCOUNT_BALANCE(bitmap);
1172
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001173 SkRect storage;
1174 const SkRect* bounds = NULL;
1175 if (paint && paint->canComputeFastBounds()) {
1176 bitmap.getBounds(&storage);
1177 matrix.mapRect(&storage);
1178 bounds = &paint->computeFastBounds(storage, &storage);
1179 }
1180
1181 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001182
1183 while (iter.next()) {
1184 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1185 }
1186
1187 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188}
1189
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001190void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001191 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 SkPaint tmp;
1193 if (NULL == paint) {
1194 tmp.setDither(true);
1195 paint = &tmp;
1196 }
reed@google.com4b226022011-01-11 18:32:13 +00001197
reed@google.com8926b162012-03-23 15:36:36 +00001198 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001200 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001201 paint = &looper.paint();
1202 SkImageFilter* filter = paint->getImageFilter();
1203 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001204 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001205 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001206 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001207 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001208 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001209 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001210 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001211 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001212 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1213 SkAutoUnref aur(NULL);
1214 if (!cache) {
1215 cache = SkImageFilter::Cache::Create();
1216 aur.reset(cache);
1217 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001218 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001219 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001220 SkPaint tmpUnfiltered(*paint);
1221 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001222 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1223 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001224 }
1225 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001226 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001227 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001229 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230}
1231
reed@google.com8926b162012-03-23 15:36:36 +00001232void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1233 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001234 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001235 return;
1236 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001237 SkDEBUGCODE(bitmap.validate();)
1238 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001239
reed@google.com8926b162012-03-23 15:36:36 +00001240 SkPaint tmp;
1241 if (NULL == paint) {
1242 paint = &tmp;
1243 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001244
reed@google.com8926b162012-03-23 15:36:36 +00001245 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001246
reed@google.com8926b162012-03-23 15:36:36 +00001247 while (iter.next()) {
1248 paint = &looper.paint();
1249 SkImageFilter* filter = paint->getImageFilter();
1250 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1251 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001252 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001253 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001254 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001255 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001256 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001257 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001258 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1259 SkAutoUnref aur(NULL);
1260 if (!cache) {
1261 cache = SkImageFilter::Cache::Create();
1262 aur.reset(cache);
1263 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001264 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001265 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001266 SkPaint tmpUnfiltered(*paint);
1267 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001268 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001269 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001270 }
1271 } else {
1272 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1273 }
1274 }
1275 LOOPER_END
1276}
1277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001279void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001280 SkMatrix m;
1281 m.setTranslate(dx, dy);
1282 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283}
1284
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001285void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001286 SkMatrix m;
1287 m.setScale(sx, sy);
1288 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289}
1290
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001291void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001292 SkMatrix m;
1293 m.setRotate(degrees);
1294 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295}
1296
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001297void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001298 SkMatrix m;
1299 m.setSkew(sx, sy);
1300 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001301}
1302
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001303void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001304 if (matrix.isIdentity()) {
1305 return;
1306 }
1307
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001309 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001310 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001311
1312 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001313}
1314
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315void SkCanvas::setMatrix(const SkMatrix& matrix) {
1316 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001317 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001319 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320}
1321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322void SkCanvas::resetMatrix() {
1323 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001324
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 matrix.reset();
1326 this->setMatrix(matrix);
1327}
1328
1329//////////////////////////////////////////////////////////////////////////////
1330
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001331void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001332 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1333 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001334}
1335
1336void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001337#ifdef SK_ENABLE_CLIP_QUICKREJECT
1338 if (SkRegion::kIntersect_Op == op) {
1339 if (fMCRec->fRasterClip->isEmpty()) {
1340 return false;
1341 }
1342
reed@google.com3b3e8952012-08-16 20:53:31 +00001343 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001344 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001345 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001346
1347 fClipStack.clipEmpty();
1348 return fMCRec->fRasterClip->setEmpty();
1349 }
1350 }
1351#endif
1352
reed@google.com5c3d1472011-02-22 19:12:23 +00001353 AutoValidateClip avc(this);
1354
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001356 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001357 if (!fAllowSoftClip) {
1358 edgeStyle = kHard_ClipEdgeStyle;
1359 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360
1361 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001362 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001363 // the matrix. This means we don't have to a) make a path, and b) tell
1364 // the region code to scan-convert the path, only to discover that it
1365 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367
1368 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001369 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1370 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001372 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001373 // and clip against that, since it can handle any matrix. However, to
1374 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1375 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376 SkPath path;
1377
1378 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001379 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 }
1381}
1382
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001383static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1384 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001385 // base is used to limit the size (and therefore memory allocation) of the
1386 // region that results from scan converting devPath.
1387 SkRegion base;
1388
reed@google.com819c9212011-02-23 18:56:55 +00001389 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001390 // since we are intersect, we can do better (tighter) with currRgn's
1391 // bounds, than just using the device. However, if currRgn is complex,
1392 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001393 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001394 // FIXME: we should also be able to do this when currClip->isBW(),
1395 // but relaxing the test above triggers GM asserts in
1396 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001397 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001398 } else {
reed@google.com00177082011-10-12 14:34:30 +00001399 base.setRect(currClip->getBounds());
1400 SkRasterClip clip;
1401 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001402 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001403 }
reed@google.com819c9212011-02-23 18:56:55 +00001404 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001405 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001406 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001407 currClip->setEmpty();
1408 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001409 }
1410
junov@chromium.orga907ac32012-02-24 21:54:07 +00001411 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001412
1413 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001414 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001415 } else {
reed@google.com00177082011-10-12 14:34:30 +00001416 SkRasterClip clip;
1417 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001418 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001419 }
1420 }
1421}
1422
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001423void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001424 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001425 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001426 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1427 } else {
1428 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001429 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001430}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001431
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001432void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001433 SkRRect transformedRRect;
1434 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1435 AutoValidateClip avc(this);
1436
1437 fDeviceCMDirty = true;
1438 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001439 if (!fAllowSoftClip) {
1440 edgeStyle = kHard_ClipEdgeStyle;
1441 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001442
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001443 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001444
1445 SkPath devPath;
1446 devPath.addRRect(transformedRRect);
1447
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001448 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1449 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001450 }
1451
1452 SkPath path;
1453 path.addRRect(rrect);
1454 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001455 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001456}
1457
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001458void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001459 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1460 SkRect r;
1461 if (!path.isInverseFillType() && path.isRect(&r)) {
1462 this->onClipRect(r, op, edgeStyle);
1463 } else {
1464 this->onClipPath(path, op, edgeStyle);
1465 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001466}
1467
1468void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001469#ifdef SK_ENABLE_CLIP_QUICKREJECT
1470 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1471 if (fMCRec->fRasterClip->isEmpty()) {
1472 return false;
1473 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001474
reed@google.com3b3e8952012-08-16 20:53:31 +00001475 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001476 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001477 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001478
reed@google.comda17f752012-08-16 18:27:05 +00001479 fClipStack.clipEmpty();
1480 return fMCRec->fRasterClip->setEmpty();
1481 }
1482 }
1483#endif
1484
reed@google.com5c3d1472011-02-22 19:12:23 +00001485 AutoValidateClip avc(this);
1486
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001488 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001489 if (!fAllowSoftClip) {
1490 edgeStyle = kHard_ClipEdgeStyle;
1491 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492
1493 SkPath devPath;
1494 path.transform(*fMCRec->fMatrix, &devPath);
1495
reed@google.comfe701122011-11-08 19:41:23 +00001496 // Check if the transfomation, or the original path itself
1497 // made us empty. Note this can also happen if we contained NaN
1498 // values. computing the bounds detects this, and will set our
1499 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1500 if (devPath.getBounds().isEmpty()) {
1501 // resetting the path will remove any NaN or other wanky values
1502 // that might upset our scan converter.
1503 devPath.reset();
1504 }
1505
reed@google.com5c3d1472011-02-22 19:12:23 +00001506 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001507 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001508
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001509 if (fAllowSimplifyClip) {
1510 devPath.reset();
1511 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1512 const SkClipStack* clipStack = getClipStack();
1513 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1514 const SkClipStack::Element* element;
1515 while ((element = iter.next())) {
1516 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001517 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001518 if (type != SkClipStack::Element::kEmpty_Type) {
1519 element->asPath(&operand);
1520 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001521 SkRegion::Op elementOp = element->getOp();
1522 if (elementOp == SkRegion::kReplace_Op) {
1523 devPath = operand;
1524 } else {
1525 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1526 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001527 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1528 // perhaps we need an API change to avoid this sort of mixed-signals about
1529 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001530 if (element->isAA()) {
1531 edgeStyle = kSoft_ClipEdgeStyle;
1532 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001533 }
1534 op = SkRegion::kReplace_Op;
1535 }
1536
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001537 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538}
1539
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001540void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001541 bool inverseFilled) {
1542 // This is for updating the clip conservatively using only bounds
1543 // information.
1544 // Contract:
1545 // The current clip must contain the true clip. The true
1546 // clip is the clip that would have normally been computed
1547 // by calls to clipPath and clipRRect
1548 // Objective:
1549 // Keep the current clip as small as possible without
1550 // breaking the contract, using only clip bounding rectangles
1551 // (for performance).
1552
1553 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1554 // don't have to worry about getting caught in a loop. Thus anywhere
1555 // we call a virtual method, we explicitly prefix it with
1556 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001557
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001558 if (inverseFilled) {
1559 switch (op) {
1560 case SkRegion::kIntersect_Op:
1561 case SkRegion::kDifference_Op:
1562 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 // the clip unchanged conservatively respects the contract.
1564 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001565 case SkRegion::kUnion_Op:
1566 case SkRegion::kReplace_Op:
1567 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001568 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001569 // These ops can grow the current clip up to the extents of
1570 // the input clip, which is inverse filled, so we just set
1571 // the current clip to the device bounds.
1572 SkRect deviceBounds;
1573 SkIRect deviceIBounds;
1574 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001575 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001576
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001577 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001578 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001579 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001580 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001581 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001582 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 break;
1584 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001585 default:
1586 SkASSERT(0); // unhandled op?
1587 }
1588 } else {
1589 // Not inverse filled
1590 switch (op) {
1591 case SkRegion::kIntersect_Op:
1592 case SkRegion::kUnion_Op:
1593 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1595 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001596 case SkRegion::kDifference_Op:
1597 // Difference can only shrink the current clip.
1598 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001599 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001600 case SkRegion::kReverseDifference_Op:
1601 // To reverse, we swap in the bounds with a replace op.
1602 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001603 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1604 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001605 case SkRegion::kXOR_Op:
1606 // Be conservative, based on (A XOR B) always included in (A union B),
1607 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001608 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1609 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001610 default:
1611 SkASSERT(0); // unhandled op?
1612 }
1613 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001614}
1615
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001616void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001617 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001618}
1619
1620void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001621 AutoValidateClip avc(this);
1622
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001624 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625
reed@google.com5c3d1472011-02-22 19:12:23 +00001626 // todo: signal fClipStack that we have a region, and therefore (I guess)
1627 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001628 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001629
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001630 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631}
1632
reed@google.com819c9212011-02-23 18:56:55 +00001633#ifdef SK_DEBUG
1634void SkCanvas::validateClip() const {
1635 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001636 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001637 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001638 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001639 return;
1640 }
1641
reed@google.com819c9212011-02-23 18:56:55 +00001642 SkIRect ir;
1643 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001644 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001645
robertphillips@google.com80214e22012-07-20 15:33:18 +00001646 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001647 const SkClipStack::Element* element;
1648 while ((element = iter.next()) != NULL) {
1649 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001650 case SkClipStack::Element::kRect_Type:
1651 element->getRect().round(&ir);
1652 tmpClip.op(ir, element->getOp());
1653 break;
1654 case SkClipStack::Element::kEmpty_Type:
1655 tmpClip.setEmpty();
1656 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001657 default: {
1658 SkPath path;
1659 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001660 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001661 break;
1662 }
reed@google.com819c9212011-02-23 18:56:55 +00001663 }
1664 }
reed@google.com819c9212011-02-23 18:56:55 +00001665}
1666#endif
1667
reed@google.com90c07ea2012-04-13 13:50:27 +00001668void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001669 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001670 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001671
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001672 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001673 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001674 }
1675}
1676
reed@google.com5c3d1472011-02-22 19:12:23 +00001677///////////////////////////////////////////////////////////////////////////////
1678
reed@google.com754de5f2014-02-24 19:38:20 +00001679bool SkCanvas::isClipEmpty() const {
1680 return fMCRec->fRasterClip->isEmpty();
1681}
1682
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001683bool SkCanvas::isClipRect() const {
1684 return fMCRec->fRasterClip->isRect();
1685}
1686
reed@google.com3b3e8952012-08-16 20:53:31 +00001687bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001688
reed@google.com16078632011-12-06 18:56:37 +00001689 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001690 return true;
1691
reed@google.com00177082011-10-12 14:34:30 +00001692 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693 return true;
1694 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695
tomhudson@google.com8d430182011-06-06 19:11:19 +00001696 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001697 SkRect dst;
1698 fMCRec->fMatrix->mapRect(&dst, rect);
1699 SkIRect idst;
1700 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001701 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001702 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001703 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001704
reed@android.coma380ae42009-07-21 01:17:02 +00001705 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001706 // TODO: should we use | instead, or compare all 4 at once?
1707 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001708 return true;
1709 }
reed@google.comc0784db2013-12-13 21:16:12 +00001710 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001711 return true;
1712 }
1713 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715}
1716
reed@google.com3b3e8952012-08-16 20:53:31 +00001717bool SkCanvas::quickReject(const SkPath& path) const {
1718 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719}
1720
reed@google.com3b3e8952012-08-16 20:53:31 +00001721bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001722 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001723 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724 return false;
1725 }
1726
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001727 SkMatrix inverse;
1728 // if we can't invert the CTM, we can't return local clip bounds
1729 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001730 if (bounds) {
1731 bounds->setEmpty();
1732 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001733 return false;
1734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001736 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001737 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001738 // adjust it outwards in case we are antialiasing
1739 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001740
reed@google.com8f4d2302013-12-17 16:44:46 +00001741 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1742 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743 inverse.mapRect(bounds, r);
1744 }
1745 return true;
1746}
1747
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001748bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001749 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001750 if (clip.isEmpty()) {
1751 if (bounds) {
1752 bounds->setEmpty();
1753 }
1754 return false;
1755 }
1756
1757 if (NULL != bounds) {
1758 *bounds = clip.getBounds();
1759 }
1760 return true;
1761}
1762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763const SkMatrix& SkCanvas::getTotalMatrix() const {
1764 return *fMCRec->fMatrix;
1765}
1766
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001767#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001768SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001769 if (fMCRec->fRasterClip->isEmpty()) {
1770 return kEmpty_ClipType;
1771 }
1772 if (fMCRec->fRasterClip->isRect()) {
1773 return kRect_ClipType;
1774 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001775 return kComplex_ClipType;
1776}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001777#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001778
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001779const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1780 return fMCRec->fRasterClip->forceGetBW();
1781}
1782
1783void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1784 path->reset();
1785
1786 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1787 if (rgn.isEmpty()) {
1788 return;
1789 }
1790 (void)rgn.getBoundaryPath(path);
1791}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792
reed@google.com9c135db2014-03-12 18:28:35 +00001793GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1794 SkBaseDevice* dev = this->getTopDevice();
1795 return dev ? dev->accessRenderTarget() : NULL;
1796}
1797
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001798SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001799 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001800 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801}
1802
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001803GrContext* SkCanvas::getGrContext() {
1804#if SK_SUPPORT_GPU
1805 SkBaseDevice* device = this->getTopDevice();
1806 if (NULL != device) {
1807 GrRenderTarget* renderTarget = device->accessRenderTarget();
1808 if (NULL != renderTarget) {
1809 return renderTarget->getContext();
1810 }
1811 }
1812#endif
1813
1814 return NULL;
1815
1816}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001817
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001818void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1819 const SkPaint& paint) {
1820 if (outer.isEmpty()) {
1821 return;
1822 }
1823 if (inner.isEmpty()) {
1824 this->drawRRect(outer, paint);
1825 return;
1826 }
1827
1828 // We don't have this method (yet), but technically this is what we should
1829 // be able to assert...
1830 // SkASSERT(outer.contains(inner));
1831 //
1832 // For now at least check for containment of bounds
1833 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1834
1835 this->onDrawDRRect(outer, inner, paint);
1836}
1837
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838//////////////////////////////////////////////////////////////////////////////
1839// These are the virtual drawing methods
1840//////////////////////////////////////////////////////////////////////////////
1841
reed@google.com2a981812011-04-14 18:59:28 +00001842void SkCanvas::clear(SkColor color) {
1843 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001844 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001845 while (iter.next()) {
1846 iter.fDevice->clear(color);
1847 }
1848}
1849
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001850void SkCanvas::onDiscard() {
1851 if (NULL != fSurfaceBase) {
1852 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1853 }
1854}
1855
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001857 this->internalDrawPaint(paint);
1858}
1859
1860void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001861 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862
1863 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001864 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865 }
1866
reed@google.com4e2b3d32011-04-07 14:18:59 +00001867 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001868}
1869
1870void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1871 const SkPaint& paint) {
1872 if ((long)count <= 0) {
1873 return;
1874 }
1875
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001876 SkRect r, storage;
1877 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001878 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001879 // special-case 2 points (common for drawing a single line)
1880 if (2 == count) {
1881 r.set(pts[0], pts[1]);
1882 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001883 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001884 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001885 bounds = &paint.computeFastStrokeBounds(r, &storage);
1886 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001887 return;
1888 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001889 }
reed@google.coma584aed2012-05-16 14:06:02 +00001890
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891 SkASSERT(pts != NULL);
1892
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001893 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001894
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001896 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001897 }
reed@google.com4b226022011-01-11 18:32:13 +00001898
reed@google.com4e2b3d32011-04-07 14:18:59 +00001899 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900}
1901
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001902void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001903 SkRect storage;
1904 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001906 bounds = &paint.computeFastBounds(r, &storage);
1907 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908 return;
1909 }
1910 }
reed@google.com4b226022011-01-11 18:32:13 +00001911
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001912 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913
1914 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001915 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916 }
1917
reed@google.com4e2b3d32011-04-07 14:18:59 +00001918 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919}
1920
reed@google.com4ed0fb72012-12-12 20:48:18 +00001921void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001922 SkRect storage;
1923 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001924 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001925 bounds = &paint.computeFastBounds(oval, &storage);
1926 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001927 return;
1928 }
1929 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001930
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001931 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001932
1933 while (iter.next()) {
1934 iter.fDevice->drawOval(iter, oval, looper.paint());
1935 }
1936
1937 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001938}
1939
1940void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001941 SkRect storage;
1942 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001943 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001944 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1945 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001946 return;
1947 }
1948 }
1949
1950 if (rrect.isRect()) {
1951 // call the non-virtual version
1952 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001953 return;
1954 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001955 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001956 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1957 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001958 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001959
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001960 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001961
1962 while (iter.next()) {
1963 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1964 }
1965
1966 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001967}
1968
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001969void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1970 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001971 SkRect storage;
1972 const SkRect* bounds = NULL;
1973 if (paint.canComputeFastBounds()) {
1974 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1975 if (this->quickReject(*bounds)) {
1976 return;
1977 }
1978 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001979
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001980 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001981
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001982 while (iter.next()) {
1983 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1984 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001985
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001986 LOOPER_END
1987}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001988
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001989void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001990 if (!path.isFinite()) {
1991 return;
1992 }
1993
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001994 SkRect storage;
1995 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001996 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001997 const SkRect& pathBounds = path.getBounds();
1998 bounds = &paint.computeFastBounds(pathBounds, &storage);
1999 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000 return;
2001 }
2002 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002003
2004 const SkRect& r = path.getBounds();
2005 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002006 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002007 this->internalDrawPaint(paint);
2008 }
2009 return;
2010 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002012 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002013
2014 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002015 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 }
2017
reed@google.com4e2b3d32011-04-07 14:18:59 +00002018 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019}
2020
2021void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2022 const SkPaint* paint) {
2023 SkDEBUGCODE(bitmap.validate();)
2024
reed@google.com3d608122011-11-21 15:16:16 +00002025 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002026 SkRect bounds = {
2027 x, y,
2028 x + SkIntToScalar(bitmap.width()),
2029 y + SkIntToScalar(bitmap.height())
2030 };
2031 if (paint) {
2032 (void)paint->computeFastBounds(bounds, &bounds);
2033 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002034 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 return;
2036 }
2037 }
reed@google.com4b226022011-01-11 18:32:13 +00002038
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039 SkMatrix matrix;
2040 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002041 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042}
2043
reed@google.com9987ec32011-09-07 11:57:52 +00002044// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002045void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002046 const SkRect& dst, const SkPaint* paint,
2047 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002048 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002049 return;
2050 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002051
reed@google.comea033602012-12-14 13:13:55 +00002052 CHECK_LOCKCOUNT_BALANCE(bitmap);
2053
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002054 SkRect storage;
2055 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002056 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002057 if (paint) {
2058 bounds = &paint->computeFastBounds(dst, &storage);
2059 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002060 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002061 return;
2062 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063 }
reed@google.com3d608122011-11-21 15:16:16 +00002064
reed@google.com33535f32012-09-25 15:37:50 +00002065 SkLazyPaint lazy;
2066 if (NULL == paint) {
2067 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002069
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002070 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002071
reed@google.com33535f32012-09-25 15:37:50 +00002072 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002073 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002074 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002075
reed@google.com33535f32012-09-25 15:37:50 +00002076 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077}
2078
reed@google.com71121732012-09-18 15:14:33 +00002079void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002080 const SkRect& dst, const SkPaint* paint,
2081 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002082 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002083 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002084}
2085
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2087 const SkPaint* paint) {
2088 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002089 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090}
2091
reed@google.com9987ec32011-09-07 11:57:52 +00002092void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2093 const SkIRect& center, const SkRect& dst,
2094 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002095 if (bitmap.drawsNothing()) {
2096 return;
2097 }
reed@google.com3d608122011-11-21 15:16:16 +00002098 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002099 SkRect storage;
2100 const SkRect* bounds = &dst;
2101 if (paint) {
2102 bounds = &paint->computeFastBounds(dst, &storage);
2103 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002104 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002105 return;
2106 }
2107 }
2108
reed@google.com9987ec32011-09-07 11:57:52 +00002109 const int32_t w = bitmap.width();
2110 const int32_t h = bitmap.height();
2111
2112 SkIRect c = center;
2113 // pin center to the bounds of the bitmap
2114 c.fLeft = SkMax32(0, center.fLeft);
2115 c.fTop = SkMax32(0, center.fTop);
2116 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2117 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2118
reed@google.com71121732012-09-18 15:14:33 +00002119 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002120 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002121 };
2122 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002123 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002124 };
reed@google.com9987ec32011-09-07 11:57:52 +00002125 SkScalar dstX[4] = {
2126 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2127 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2128 };
2129 SkScalar dstY[4] = {
2130 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2131 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2132 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002133
reed@google.com9987ec32011-09-07 11:57:52 +00002134 if (dstX[1] > dstX[2]) {
2135 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2136 dstX[2] = dstX[1];
2137 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002138
reed@google.com9987ec32011-09-07 11:57:52 +00002139 if (dstY[1] > dstY[2]) {
2140 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2141 dstY[2] = dstY[1];
2142 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002143
reed@google.com9987ec32011-09-07 11:57:52 +00002144 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002145 SkRect s, d;
2146
reed@google.com9987ec32011-09-07 11:57:52 +00002147 s.fTop = srcY[y];
2148 s.fBottom = srcY[y+1];
2149 d.fTop = dstY[y];
2150 d.fBottom = dstY[y+1];
2151 for (int x = 0; x < 3; x++) {
2152 s.fLeft = srcX[x];
2153 s.fRight = srcX[x+1];
2154 d.fLeft = dstX[x];
2155 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002156 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002157 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002158 }
2159 }
2160}
2161
2162void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2163 const SkRect& dst, const SkPaint* paint) {
2164 SkDEBUGCODE(bitmap.validate();)
2165
2166 // Need a device entry-point, so gpu can use a mesh
2167 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2168}
2169
reed@google.comf67e4cf2011-03-15 20:56:58 +00002170class SkDeviceFilteredPaint {
2171public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002172 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2173 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002174 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002175 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002176 newPaint->setFlags(flags.fFlags);
2177 newPaint->setHinting(flags.fHinting);
2178 fPaint = newPaint;
2179 } else {
2180 fPaint = &paint;
2181 }
2182 }
2183
reed@google.comf67e4cf2011-03-15 20:56:58 +00002184 const SkPaint& paint() const { return *fPaint; }
2185
2186private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002187 const SkPaint* fPaint;
2188 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002189};
2190
bungeman@google.com52c748b2011-08-22 21:30:43 +00002191void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2192 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002193 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002194 draw.fDevice->drawRect(draw, r, paint);
2195 } else {
2196 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002197 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002198 draw.fDevice->drawRect(draw, r, p);
2199 }
2200}
2201
2202void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2203 const char text[], size_t byteLength,
2204 SkScalar x, SkScalar y) {
2205 SkASSERT(byteLength == 0 || text != NULL);
2206
2207 // nothing to draw
2208 if (text == NULL || byteLength == 0 ||
2209 draw.fClip->isEmpty() ||
2210 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2211 return;
2212 }
2213
2214 SkScalar width = 0;
2215 SkPoint start;
2216
2217 start.set(0, 0); // to avoid warning
2218 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2219 SkPaint::kStrikeThruText_Flag)) {
2220 width = paint.measureText(text, byteLength);
2221
2222 SkScalar offsetX = 0;
2223 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2224 offsetX = SkScalarHalf(width);
2225 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2226 offsetX = width;
2227 }
2228 start.set(x - offsetX, y);
2229 }
2230
2231 if (0 == width) {
2232 return;
2233 }
2234
2235 uint32_t flags = paint.getFlags();
2236
2237 if (flags & (SkPaint::kUnderlineText_Flag |
2238 SkPaint::kStrikeThruText_Flag)) {
2239 SkScalar textSize = paint.getTextSize();
2240 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2241 SkRect r;
2242
2243 r.fLeft = start.fX;
2244 r.fRight = start.fX + width;
2245
2246 if (flags & SkPaint::kUnderlineText_Flag) {
2247 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2248 start.fY);
2249 r.fTop = offset;
2250 r.fBottom = offset + height;
2251 DrawRect(draw, paint, r, textSize);
2252 }
2253 if (flags & SkPaint::kStrikeThruText_Flag) {
2254 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2255 start.fY);
2256 r.fTop = offset;
2257 r.fBottom = offset + height;
2258 DrawRect(draw, paint, r, textSize);
2259 }
2260 }
2261}
2262
reed@google.come0d9ce82014-04-23 04:00:17 +00002263void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2264 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002265 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266
2267 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002268 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002269 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002270 DrawTextDecorations(iter, dfp.paint(),
2271 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 }
2273
reed@google.com4e2b3d32011-04-07 14:18:59 +00002274 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275}
2276
reed@google.come0d9ce82014-04-23 04:00:17 +00002277void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2278 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002279 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002280
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002282 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002284 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002285 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002286
reed@google.com4e2b3d32011-04-07 14:18:59 +00002287 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288}
2289
reed@google.come0d9ce82014-04-23 04:00:17 +00002290void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2291 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002292 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002293
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002295 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002296 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002297 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002298 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002299
reed@google.com4e2b3d32011-04-07 14:18:59 +00002300 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002301}
2302
reed@google.come0d9ce82014-04-23 04:00:17 +00002303void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2304 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002305 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002306
reed@android.com8a1c16f2008-12-17 15:59:43 +00002307 while (iter.next()) {
2308 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002309 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002311
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002312 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002313}
2314
reed@google.come0d9ce82014-04-23 04:00:17 +00002315// These will become non-virtual, so they always call the (virtual) onDraw... method
2316void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2317 const SkPaint& paint) {
2318 this->onDrawText(text, byteLength, x, y, paint);
2319}
2320void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2321 const SkPaint& paint) {
2322 this->onDrawPosText(text, byteLength, pos, paint);
2323}
2324void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2325 SkScalar constY, const SkPaint& paint) {
2326 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2327}
2328void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2329 const SkMatrix* matrix, const SkPaint& paint) {
2330 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2331}
2332
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2334 const SkPoint verts[], const SkPoint texs[],
2335 const SkColor colors[], SkXfermode* xmode,
2336 const uint16_t indices[], int indexCount,
2337 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002338 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002339
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 while (iter.next()) {
2341 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002342 colors, xmode, indices, indexCount,
2343 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 }
reed@google.com4b226022011-01-11 18:32:13 +00002345
reed@google.com4e2b3d32011-04-07 14:18:59 +00002346 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002347}
2348
2349//////////////////////////////////////////////////////////////////////////////
2350// These methods are NOT virtual, and therefore must call back into virtual
2351// methods, rather than actually drawing themselves.
2352//////////////////////////////////////////////////////////////////////////////
2353
2354void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002355 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 SkPaint paint;
2357
2358 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002359 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002360 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002361 }
2362 this->drawPaint(paint);
2363}
2364
reed@android.com845fdac2009-06-23 03:01:32 +00002365void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 SkPaint paint;
2367
2368 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002369 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002370 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 }
2372 this->drawPaint(paint);
2373}
2374
2375void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2376 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002377
reed@android.com8a1c16f2008-12-17 15:59:43 +00002378 pt.set(x, y);
2379 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2380}
2381
2382void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2383 SkPoint pt;
2384 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002385
reed@android.com8a1c16f2008-12-17 15:59:43 +00002386 pt.set(x, y);
2387 paint.setColor(color);
2388 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2389}
2390
2391void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2392 const SkPaint& paint) {
2393 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002394
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395 pts[0].set(x0, y0);
2396 pts[1].set(x1, y1);
2397 this->drawPoints(kLines_PointMode, 2, pts, paint);
2398}
2399
2400void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2401 SkScalar right, SkScalar bottom,
2402 const SkPaint& paint) {
2403 SkRect r;
2404
2405 r.set(left, top, right, bottom);
2406 this->drawRect(r, paint);
2407}
2408
2409void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2410 const SkPaint& paint) {
2411 if (radius < 0) {
2412 radius = 0;
2413 }
2414
2415 SkRect r;
2416 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002417 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002418}
2419
2420void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2421 const SkPaint& paint) {
2422 if (rx > 0 && ry > 0) {
2423 if (paint.canComputeFastBounds()) {
2424 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002425 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426 return;
2427 }
2428 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002429 SkRRect rrect;
2430 rrect.setRectXY(r, rx, ry);
2431 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432 } else {
2433 this->drawRect(r, paint);
2434 }
2435}
2436
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2438 SkScalar sweepAngle, bool useCenter,
2439 const SkPaint& paint) {
2440 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2441 this->drawOval(oval, paint);
2442 } else {
2443 SkPath path;
2444 if (useCenter) {
2445 path.moveTo(oval.centerX(), oval.centerY());
2446 }
2447 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2448 if (useCenter) {
2449 path.close();
2450 }
2451 this->drawPath(path, paint);
2452 }
2453}
2454
2455void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2456 const SkPath& path, SkScalar hOffset,
2457 SkScalar vOffset, const SkPaint& paint) {
2458 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002459
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460 matrix.setTranslate(hOffset, vOffset);
2461 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2462}
2463
reed@android.comf76bacf2009-05-13 14:00:33 +00002464///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002465void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002466 SkBaseDevice* device = this->getDevice();
2467 if (NULL != device) {
2468 device->EXPERIMENTAL_optimize(picture);
2469 }
2470}
reed@android.comf76bacf2009-05-13 14:00:33 +00002471
robertphillips9b14f262014-06-04 05:40:44 -07002472void SkCanvas::EXPERIMENTAL_purge(const SkPicture* picture) {
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002473 SkBaseDevice* device = this->getTopDevice();
2474 if (NULL != device) {
2475 device->EXPERIMENTAL_purge(picture);
2476 }
2477}
2478
robertphillips9b14f262014-06-04 05:40:44 -07002479void SkCanvas::drawPicture(const SkPicture* picture) {
2480 if (NULL != picture) {
2481 this->onDrawPicture(picture);
2482 }
2483}
2484
2485void SkCanvas::onDrawPicture(const SkPicture* picture) {
2486 SkASSERT(NULL != picture);
2487
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002488 SkBaseDevice* device = this->getTopDevice();
2489 if (NULL != device) {
2490 // Canvas has to first give the device the opportunity to render
2491 // the picture itself.
robertphillips9b14f262014-06-04 05:40:44 -07002492 if (device->EXPERIMENTAL_drawPicture(this, picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002493 return; // the device has rendered the entire picture
2494 }
2495 }
2496
robertphillips9b14f262014-06-04 05:40:44 -07002497 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002498}
2499
reed@android.com8a1c16f2008-12-17 15:59:43 +00002500///////////////////////////////////////////////////////////////////////////////
2501///////////////////////////////////////////////////////////////////////////////
2502
2503SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002504 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505
2506 SkASSERT(canvas);
2507
2508 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2509 fDone = !fImpl->next();
2510}
2511
2512SkCanvas::LayerIter::~LayerIter() {
2513 fImpl->~SkDrawIter();
2514}
2515
2516void SkCanvas::LayerIter::next() {
2517 fDone = !fImpl->next();
2518}
2519
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002520SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521 return fImpl->getDevice();
2522}
2523
2524const SkMatrix& SkCanvas::LayerIter::matrix() const {
2525 return fImpl->getMatrix();
2526}
2527
2528const SkPaint& SkCanvas::LayerIter::paint() const {
2529 const SkPaint* paint = fImpl->getPaint();
2530 if (NULL == paint) {
2531 paint = &fDefaultPaint;
2532 }
2533 return *paint;
2534}
2535
2536const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2537int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2538int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002539
2540///////////////////////////////////////////////////////////////////////////////
2541
fmalitac3b589a2014-06-05 12:40:07 -07002542SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002543
2544///////////////////////////////////////////////////////////////////////////////
2545
2546static bool supported_for_raster_canvas(const SkImageInfo& info) {
2547 switch (info.alphaType()) {
2548 case kPremul_SkAlphaType:
2549 case kOpaque_SkAlphaType:
2550 break;
2551 default:
2552 return false;
2553 }
2554
2555 switch (info.colorType()) {
2556 case kAlpha_8_SkColorType:
2557 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002558 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002559 break;
2560 default:
2561 return false;
2562 }
2563
2564 return true;
2565}
2566
2567SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2568 if (!supported_for_raster_canvas(info)) {
2569 return NULL;
2570 }
2571
2572 SkBitmap bitmap;
2573 if (!bitmap.allocPixels(info)) {
2574 return NULL;
2575 }
2576
2577 // should this functionality be moved into allocPixels()?
2578 if (!bitmap.info().isOpaque()) {
2579 bitmap.eraseColor(0);
2580 }
2581 return SkNEW_ARGS(SkCanvas, (bitmap));
2582}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002583
2584SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2585 if (!supported_for_raster_canvas(info)) {
2586 return NULL;
2587 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002588
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002589 SkBitmap bitmap;
2590 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2591 return NULL;
2592 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002593 return SkNEW_ARGS(SkCanvas, (bitmap));
2594}