blob: 3134c4668689b75323efb5de155037955bfbbe64 [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
epoger@google.comec3ed6a2011-07-28 14:26:00 +00008
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
756 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000757 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000758}
reed@google.com51df9e32010-12-23 19:29:18 +0000759
junov@google.com4370aed2012-01-18 16:21:08 +0000760SkCanvas* SkCanvas::canvasForDrawIter() {
761 return this;
762}
763
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764//////////////////////////////////////////////////////////////////////////////
765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766void SkCanvas::updateDeviceCMCache() {
767 if (fDeviceCMDirty) {
768 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000769 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000773 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000775 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000777 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 } while ((layer = layer->fNext) != NULL);
779 }
780 fDeviceCMDirty = false;
781 }
782}
783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784///////////////////////////////////////////////////////////////////////////////
785
786int SkCanvas::internalSave(SaveFlags flags) {
787 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 MCRec* newTop = (MCRec*)fMCStack.push_back();
790 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000793
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000794 if (SkCanvas::kClip_SaveFlag & flags) {
795 fClipStack.save();
796 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 return saveCount;
799}
800
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000801int SkCanvas::save() {
802 this->willSave(kMatrixClip_SaveFlag);
803 return this->internalSave(kMatrixClip_SaveFlag);
804}
805
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000807 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 // call shared impl
809 return this->internalSave(flags);
810}
811
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000813#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000815#else
816 return true;
817#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818}
819
junov@chromium.orga907ac32012-02-24 21:54:07 +0000820bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000821 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000822 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000823 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000824 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000825 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000826 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000827
828 if (imageFilter) {
829 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
830 // Filters may grow the bounds beyond the device bounds.
831 op = SkRegion::kReplace_Op;
832 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000833 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 if (NULL != bounds) {
835 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000836
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 this->getTotalMatrix().mapRect(&r, *bounds);
838 r.roundOut(&ir);
839 // early exit if the layer's bounds are clipped out
840 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000841 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000842 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000843 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000844 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 }
846 } else { // no user bounds, so just use the clip
847 ir = clipBounds;
848 }
849
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000850 if (bounds_affects_clip(flags)) {
851 fClipStack.clipDevRect(ir, op);
852 // early exit if the clip is now empty
853 if (!fMCRec->fRasterClip->op(ir, op)) {
854 return false;
855 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856 }
857
858 if (intersection) {
859 *intersection = ir;
860 }
861 return true;
862}
863
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000864int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
865 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
866 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
867}
868
junov@chromium.orga907ac32012-02-24 21:54:07 +0000869int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
870 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000871 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
872 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000873}
874
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000875static SkBaseDevice* create_compatible_device(SkCanvas* canvas,
876 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000877 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000878 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000879}
880
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000881int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
882 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000883#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000884 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000885#endif
886
junov@chromium.orga907ac32012-02-24 21:54:07 +0000887 // do this before we create the layer. We don't call the public save() since
888 // that would invoke a possibly overridden virtual
889 int count = this->internalSave(flags);
890
891 fDeviceCMDirty = true;
892
893 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000894 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 return count;
896 }
897
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000898 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
899 // the clipRectBounds() call above?
900 if (kNoLayer_SaveLayerStrategy == strategy) {
901 return count;
902 }
903
reed@google.comb55deeb2012-01-06 14:43:09 +0000904 // Kill the imagefilter if our device doesn't allow it
905 SkLazyPaint lazyP;
906 if (paint && paint->getImageFilter()) {
907 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000908 if (justForImageFilter) {
909 // early exit if the layer was just for the imageFilter
910 return count;
911 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000912 SkPaint* p = lazyP.set(*paint);
913 p->setImageFilter(NULL);
914 paint = p;
915 }
916 }
917
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000918 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
919 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
920 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000922 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000923 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000924 device = create_compatible_device(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000925 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000926 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000927 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000928 if (NULL == device) {
929 SkDebugf("Unable to create device for layer.");
930 return count;
931 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000932
reed@google.com6f8f2922011-03-04 22:27:10 +0000933 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000934 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 device->unref();
936
937 layer->fNext = fMCRec->fTopLayer;
938 fMCRec->fLayer = layer;
939 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
940
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000941 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 return count;
943}
944
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000945int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
946 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
947}
948
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
950 SaveFlags flags) {
951 if (0xFF == alpha) {
952 return this->saveLayer(bounds, NULL, flags);
953 } else {
954 SkPaint tmpPaint;
955 tmpPaint.setAlpha(alpha);
956 return this->saveLayer(bounds, &tmpPaint, flags);
957 }
958}
959
960void SkCanvas::restore() {
961 // check for underflow
962 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000963 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 this->internalRestore();
965 }
966}
967
968void SkCanvas::internalRestore() {
969 SkASSERT(fMCStack.count() != 0);
970
971 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000972 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000974 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
975 fClipStack.restore();
976 }
977
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000978 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 DeviceCM* layer = fMCRec->fLayer; // may be null
980 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
981 fMCRec->fLayer = NULL;
982
983 // now do the normal restore()
984 fMCRec->~MCRec(); // balanced in save()
985 fMCStack.pop_back();
986 fMCRec = (MCRec*)fMCStack.back();
987
988 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
989 since if we're being recorded, we don't want to record this (the
990 recorder will have already recorded the restore).
991 */
992 if (NULL != layer) {
993 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000994 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000995 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
996 layer->fPaint);
997 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000999
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001000 SkASSERT(fSaveLayerCount > 0);
1001 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 }
1003 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001004 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005}
1006
1007int SkCanvas::getSaveCount() const {
1008 return fMCStack.count();
1009}
1010
1011void SkCanvas::restoreToCount(int count) {
1012 // sanity check
1013 if (count < 1) {
1014 count = 1;
1015 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001016
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001017 int n = this->getSaveCount() - count;
1018 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 this->restore();
1020 }
1021}
1022
reed@google.com7c202932011-12-14 18:48:05 +00001023bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001024 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001025}
1026
reed@google.com76f10a32014-02-05 15:32:21 +00001027SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1028 return this->onNewSurface(info);
1029}
1030
1031SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1032 SkBaseDevice* dev = this->getDevice();
1033 return dev ? dev->newSurface(info) : NULL;
1034}
1035
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001036SkImageInfo SkCanvas::imageInfo() const {
1037 SkBaseDevice* dev = this->getDevice();
1038 if (dev) {
1039 return dev->imageInfo();
1040 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001041 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001042 }
1043}
1044
1045const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1046 return this->onPeekPixels(info, rowBytes);
1047}
1048
1049const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1050 SkBaseDevice* dev = this->getDevice();
1051 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1052}
1053
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001054void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1055 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1056 if (pixels && origin) {
1057 *origin = this->getTopDevice(false)->getOrigin();
1058 }
1059 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001060}
1061
1062void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1063 SkBaseDevice* dev = this->getTopDevice();
1064 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1065}
1066
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001067SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1068 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1069 if (NULL == fAddr) {
1070 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001071 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001072 return; // failure, fAddr is NULL
1073 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001074 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1075 return; // failure, fAddr is NULL
1076 }
1077 fAddr = fBitmap.getPixels();
1078 fRowBytes = fBitmap.rowBytes();
1079 }
1080 SkASSERT(fAddr); // success
1081}
1082
1083bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1084 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001085 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001086 } else {
1087 bitmap->reset();
1088 return false;
1089 }
1090}
1091
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001092void SkCanvas::onPushCull(const SkRect& cullRect) {
1093 // do nothing. Subclasses may do something
1094}
1095
1096void SkCanvas::onPopCull() {
1097 // do nothing. Subclasses may do something
1098}
1099
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001101#ifdef SK_DEBUG
1102// Ensure that cull rects are monotonically nested in device space.
1103void SkCanvas::validateCull(const SkIRect& devCull) {
1104 if (fCullStack.isEmpty()
1105 || devCull.isEmpty()
1106 || fCullStack.top().contains(devCull)) {
1107 return;
1108 }
1109
1110 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1111 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1112 fCullStack.top().x(), fCullStack.top().y(),
1113 fCullStack.top().right(), fCullStack.top().bottom()));
1114
1115#ifdef ASSERT_NESTED_CULLING
1116 SkDEBUGFAIL("Invalid cull.");
1117#endif
1118}
1119#endif
1120
1121void SkCanvas::pushCull(const SkRect& cullRect) {
1122 ++fCullCount;
1123 this->onPushCull(cullRect);
1124
1125#ifdef SK_DEBUG
1126 // Map the cull rect into device space.
1127 SkRect mappedCull;
1128 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1129
1130 // Take clipping into account.
1131 SkIRect devClip, devCull;
1132 mappedCull.roundOut(&devCull);
1133 this->getClipDeviceBounds(&devClip);
1134 if (!devCull.intersect(devClip)) {
1135 devCull.setEmpty();
1136 }
1137
1138 this->validateCull(devCull);
1139 fCullStack.push(devCull); // balanced in popCull
1140#endif
1141}
1142
1143void SkCanvas::popCull() {
1144 SkASSERT(fCullStack.count() == fCullCount);
1145
1146 if (fCullCount > 0) {
1147 --fCullCount;
1148 this->onPopCull();
1149
1150 SkDEBUGCODE(fCullStack.pop());
1151 }
1152}
1153
1154/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001156void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001158 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159 return;
1160 }
1161
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001162 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001164 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001166
1167 SkDEBUGCODE(bitmap.validate();)
1168 CHECK_LOCKCOUNT_BALANCE(bitmap);
1169
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001170 SkRect storage;
1171 const SkRect* bounds = NULL;
1172 if (paint && paint->canComputeFastBounds()) {
1173 bitmap.getBounds(&storage);
1174 matrix.mapRect(&storage);
1175 bounds = &paint->computeFastBounds(storage, &storage);
1176 }
1177
1178 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001179
1180 while (iter.next()) {
1181 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1182 }
1183
1184 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185}
1186
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001187void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001188 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 SkPaint tmp;
1190 if (NULL == paint) {
1191 tmp.setDither(true);
1192 paint = &tmp;
1193 }
reed@google.com4b226022011-01-11 18:32:13 +00001194
reed@google.com8926b162012-03-23 15:36:36 +00001195 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001197 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001198 paint = &looper.paint();
1199 SkImageFilter* filter = paint->getImageFilter();
1200 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001201 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001202 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001203 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001204 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001205 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001206 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001207 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001208 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001209 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1210 SkAutoUnref aur(NULL);
1211 if (!cache) {
1212 cache = SkImageFilter::Cache::Create();
1213 aur.reset(cache);
1214 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001215 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001216 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001217 SkPaint tmpUnfiltered(*paint);
1218 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001219 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1220 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001221 }
1222 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001223 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001224 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001226 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227}
1228
reed@google.com8926b162012-03-23 15:36:36 +00001229void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1230 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001231 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001232 return;
1233 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001234 SkDEBUGCODE(bitmap.validate();)
1235 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001236
reed@google.com8926b162012-03-23 15:36:36 +00001237 SkPaint tmp;
1238 if (NULL == paint) {
1239 paint = &tmp;
1240 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001241
reed@google.com8926b162012-03-23 15:36:36 +00001242 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001243
reed@google.com8926b162012-03-23 15:36:36 +00001244 while (iter.next()) {
1245 paint = &looper.paint();
1246 SkImageFilter* filter = paint->getImageFilter();
1247 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1248 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001249 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001250 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001251 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001252 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001253 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001254 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001255 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1256 SkAutoUnref aur(NULL);
1257 if (!cache) {
1258 cache = SkImageFilter::Cache::Create();
1259 aur.reset(cache);
1260 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001261 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001262 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001263 SkPaint tmpUnfiltered(*paint);
1264 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001265 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001266 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001267 }
1268 } else {
1269 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1270 }
1271 }
1272 LOOPER_END
1273}
1274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001276void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001277 SkMatrix m;
1278 m.setTranslate(dx, dy);
1279 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280}
1281
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001282void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001283 SkMatrix m;
1284 m.setScale(sx, sy);
1285 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286}
1287
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001288void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001289 SkMatrix m;
1290 m.setRotate(degrees);
1291 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292}
1293
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001294void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001295 SkMatrix m;
1296 m.setSkew(sx, sy);
1297 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001298}
1299
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001300void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001301 if (matrix.isIdentity()) {
1302 return;
1303 }
1304
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001306 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001307 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001308
1309 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001310}
1311
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312void SkCanvas::setMatrix(const SkMatrix& matrix) {
1313 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001314 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001316 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317}
1318
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319void SkCanvas::resetMatrix() {
1320 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 matrix.reset();
1323 this->setMatrix(matrix);
1324}
1325
1326//////////////////////////////////////////////////////////////////////////////
1327
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001328void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001329 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1330 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001331}
1332
1333void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001334#ifdef SK_ENABLE_CLIP_QUICKREJECT
1335 if (SkRegion::kIntersect_Op == op) {
1336 if (fMCRec->fRasterClip->isEmpty()) {
1337 return false;
1338 }
1339
reed@google.com3b3e8952012-08-16 20:53:31 +00001340 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001341 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001342 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001343
1344 fClipStack.clipEmpty();
1345 return fMCRec->fRasterClip->setEmpty();
1346 }
1347 }
1348#endif
1349
reed@google.com5c3d1472011-02-22 19:12:23 +00001350 AutoValidateClip avc(this);
1351
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001353 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001354 if (!fAllowSoftClip) {
1355 edgeStyle = kHard_ClipEdgeStyle;
1356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357
1358 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001359 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001360 // the matrix. This means we don't have to a) make a path, and b) tell
1361 // the region code to scan-convert the path, only to discover that it
1362 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364
1365 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001366 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1367 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001369 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001370 // and clip against that, since it can handle any matrix. However, to
1371 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1372 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 SkPath path;
1374
1375 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001376 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 }
1378}
1379
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1381 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001382 // base is used to limit the size (and therefore memory allocation) of the
1383 // region that results from scan converting devPath.
1384 SkRegion base;
1385
reed@google.com819c9212011-02-23 18:56:55 +00001386 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001387 // since we are intersect, we can do better (tighter) with currRgn's
1388 // bounds, than just using the device. However, if currRgn is complex,
1389 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001390 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001391 // FIXME: we should also be able to do this when currClip->isBW(),
1392 // but relaxing the test above triggers GM asserts in
1393 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001394 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001395 } else {
reed@google.com00177082011-10-12 14:34:30 +00001396 base.setRect(currClip->getBounds());
1397 SkRasterClip clip;
1398 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001399 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001400 }
reed@google.com819c9212011-02-23 18:56:55 +00001401 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001402 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001403 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001404 currClip->setEmpty();
1405 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001406 }
1407
junov@chromium.orga907ac32012-02-24 21:54:07 +00001408 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001409
1410 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001411 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001412 } else {
reed@google.com00177082011-10-12 14:34:30 +00001413 SkRasterClip clip;
1414 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001415 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001416 }
1417 }
1418}
1419
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001420void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001422 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001423 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1424 } else {
1425 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001426 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001427}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001428
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001429void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001430 SkRRect transformedRRect;
1431 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1432 AutoValidateClip avc(this);
1433
1434 fDeviceCMDirty = true;
1435 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001436 if (!fAllowSoftClip) {
1437 edgeStyle = kHard_ClipEdgeStyle;
1438 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001439
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001440 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001441
1442 SkPath devPath;
1443 devPath.addRRect(transformedRRect);
1444
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001445 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1446 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001447 }
1448
1449 SkPath path;
1450 path.addRRect(rrect);
1451 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001452 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001453}
1454
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001455void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001456 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1457 SkRect r;
1458 if (!path.isInverseFillType() && path.isRect(&r)) {
1459 this->onClipRect(r, op, edgeStyle);
1460 } else {
1461 this->onClipPath(path, op, edgeStyle);
1462 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001463}
1464
1465void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001466#ifdef SK_ENABLE_CLIP_QUICKREJECT
1467 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1468 if (fMCRec->fRasterClip->isEmpty()) {
1469 return false;
1470 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001471
reed@google.com3b3e8952012-08-16 20:53:31 +00001472 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001473 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001474 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001475
reed@google.comda17f752012-08-16 18:27:05 +00001476 fClipStack.clipEmpty();
1477 return fMCRec->fRasterClip->setEmpty();
1478 }
1479 }
1480#endif
1481
reed@google.com5c3d1472011-02-22 19:12:23 +00001482 AutoValidateClip avc(this);
1483
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001485 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001486 if (!fAllowSoftClip) {
1487 edgeStyle = kHard_ClipEdgeStyle;
1488 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489
1490 SkPath devPath;
1491 path.transform(*fMCRec->fMatrix, &devPath);
1492
reed@google.comfe701122011-11-08 19:41:23 +00001493 // Check if the transfomation, or the original path itself
1494 // made us empty. Note this can also happen if we contained NaN
1495 // values. computing the bounds detects this, and will set our
1496 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1497 if (devPath.getBounds().isEmpty()) {
1498 // resetting the path will remove any NaN or other wanky values
1499 // that might upset our scan converter.
1500 devPath.reset();
1501 }
1502
reed@google.com5c3d1472011-02-22 19:12:23 +00001503 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001504 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001505
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001506 if (fAllowSimplifyClip) {
1507 devPath.reset();
1508 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1509 const SkClipStack* clipStack = getClipStack();
1510 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1511 const SkClipStack::Element* element;
1512 while ((element = iter.next())) {
1513 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001514 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001515 if (type != SkClipStack::Element::kEmpty_Type) {
1516 element->asPath(&operand);
1517 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001518 SkRegion::Op elementOp = element->getOp();
1519 if (elementOp == SkRegion::kReplace_Op) {
1520 devPath = operand;
1521 } else {
1522 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1523 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001524 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1525 // perhaps we need an API change to avoid this sort of mixed-signals about
1526 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001527 if (element->isAA()) {
1528 edgeStyle = kSoft_ClipEdgeStyle;
1529 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001530 }
1531 op = SkRegion::kReplace_Op;
1532 }
1533
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001534 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535}
1536
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001537void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001538 bool inverseFilled) {
1539 // This is for updating the clip conservatively using only bounds
1540 // information.
1541 // Contract:
1542 // The current clip must contain the true clip. The true
1543 // clip is the clip that would have normally been computed
1544 // by calls to clipPath and clipRRect
1545 // Objective:
1546 // Keep the current clip as small as possible without
1547 // breaking the contract, using only clip bounding rectangles
1548 // (for performance).
1549
1550 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1551 // don't have to worry about getting caught in a loop. Thus anywhere
1552 // we call a virtual method, we explicitly prefix it with
1553 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001554
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001555 if (inverseFilled) {
1556 switch (op) {
1557 case SkRegion::kIntersect_Op:
1558 case SkRegion::kDifference_Op:
1559 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001560 // the clip unchanged conservatively respects the contract.
1561 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001562 case SkRegion::kUnion_Op:
1563 case SkRegion::kReplace_Op:
1564 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001566 // These ops can grow the current clip up to the extents of
1567 // the input clip, which is inverse filled, so we just set
1568 // the current clip to the device bounds.
1569 SkRect deviceBounds;
1570 SkIRect deviceIBounds;
1571 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001572 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001573
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001574 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001575 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001576 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001577 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001578 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001579 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001580 break;
1581 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001582 default:
1583 SkASSERT(0); // unhandled op?
1584 }
1585 } else {
1586 // Not inverse filled
1587 switch (op) {
1588 case SkRegion::kIntersect_Op:
1589 case SkRegion::kUnion_Op:
1590 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001591 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1592 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001593 case SkRegion::kDifference_Op:
1594 // Difference can only shrink the current clip.
1595 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001597 case SkRegion::kReverseDifference_Op:
1598 // To reverse, we swap in the bounds with a replace op.
1599 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1601 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001602 case SkRegion::kXOR_Op:
1603 // Be conservative, based on (A XOR B) always included in (A union B),
1604 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001605 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1606 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001607 default:
1608 SkASSERT(0); // unhandled op?
1609 }
1610 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001611}
1612
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001613void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001615}
1616
1617void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001618 AutoValidateClip avc(this);
1619
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001621 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622
reed@google.com5c3d1472011-02-22 19:12:23 +00001623 // todo: signal fClipStack that we have a region, and therefore (I guess)
1624 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001625 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001626
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628}
1629
reed@google.com819c9212011-02-23 18:56:55 +00001630#ifdef SK_DEBUG
1631void SkCanvas::validateClip() const {
1632 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001633 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001634 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001635 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001636 return;
1637 }
1638
reed@google.com819c9212011-02-23 18:56:55 +00001639 SkIRect ir;
1640 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001641 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001642
robertphillips@google.com80214e22012-07-20 15:33:18 +00001643 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001644 const SkClipStack::Element* element;
1645 while ((element = iter.next()) != NULL) {
1646 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001647 case SkClipStack::Element::kRect_Type:
1648 element->getRect().round(&ir);
1649 tmpClip.op(ir, element->getOp());
1650 break;
1651 case SkClipStack::Element::kEmpty_Type:
1652 tmpClip.setEmpty();
1653 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001654 default: {
1655 SkPath path;
1656 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001657 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001658 break;
1659 }
reed@google.com819c9212011-02-23 18:56:55 +00001660 }
1661 }
reed@google.com819c9212011-02-23 18:56:55 +00001662}
1663#endif
1664
reed@google.com90c07ea2012-04-13 13:50:27 +00001665void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001666 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001667 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001668
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001669 static const SkRect kEmpty = { 0, 0, 0, 0 };
1670 while ((element = iter.next()) != NULL) {
1671 switch (element->getType()) {
1672 case SkClipStack::Element::kPath_Type:
1673 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1674 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001675 case SkClipStack::Element::kRRect_Type:
1676 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1677 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001678 case SkClipStack::Element::kRect_Type:
1679 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1680 break;
1681 case SkClipStack::Element::kEmpty_Type:
1682 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1683 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001684 }
1685 }
1686}
1687
reed@google.com5c3d1472011-02-22 19:12:23 +00001688///////////////////////////////////////////////////////////////////////////////
1689
reed@google.com754de5f2014-02-24 19:38:20 +00001690bool SkCanvas::isClipEmpty() const {
1691 return fMCRec->fRasterClip->isEmpty();
1692}
1693
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001694bool SkCanvas::isClipRect() const {
1695 return fMCRec->fRasterClip->isRect();
1696}
1697
reed@google.com3b3e8952012-08-16 20:53:31 +00001698bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001699
reed@google.com16078632011-12-06 18:56:37 +00001700 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001701 return true;
1702
reed@google.com00177082011-10-12 14:34:30 +00001703 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 return true;
1705 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706
tomhudson@google.com8d430182011-06-06 19:11:19 +00001707 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001708 SkRect dst;
1709 fMCRec->fMatrix->mapRect(&dst, rect);
1710 SkIRect idst;
1711 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001712 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001713 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001714 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001715
reed@android.coma380ae42009-07-21 01:17:02 +00001716 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001717 // TODO: should we use | instead, or compare all 4 at once?
1718 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001719 return true;
1720 }
reed@google.comc0784db2013-12-13 21:16:12 +00001721 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001722 return true;
1723 }
1724 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726}
1727
reed@google.com3b3e8952012-08-16 20:53:31 +00001728bool SkCanvas::quickReject(const SkPath& path) const {
1729 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730}
1731
reed@google.com3b3e8952012-08-16 20:53:31 +00001732bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001733 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001734 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735 return false;
1736 }
1737
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001738 SkMatrix inverse;
1739 // if we can't invert the CTM, we can't return local clip bounds
1740 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001741 if (bounds) {
1742 bounds->setEmpty();
1743 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001744 return false;
1745 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001747 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001748 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001749 // adjust it outwards in case we are antialiasing
1750 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001751
reed@google.com8f4d2302013-12-17 16:44:46 +00001752 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1753 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 inverse.mapRect(bounds, r);
1755 }
1756 return true;
1757}
1758
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001759bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001760 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001761 if (clip.isEmpty()) {
1762 if (bounds) {
1763 bounds->setEmpty();
1764 }
1765 return false;
1766 }
1767
1768 if (NULL != bounds) {
1769 *bounds = clip.getBounds();
1770 }
1771 return true;
1772}
1773
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774const SkMatrix& SkCanvas::getTotalMatrix() const {
1775 return *fMCRec->fMatrix;
1776}
1777
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001778#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001779SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001780 if (fMCRec->fRasterClip->isEmpty()) {
1781 return kEmpty_ClipType;
1782 }
1783 if (fMCRec->fRasterClip->isRect()) {
1784 return kRect_ClipType;
1785 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001786 return kComplex_ClipType;
1787}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001788#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001789
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001790#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001792 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001794#endif
1795
1796const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1797 return fMCRec->fRasterClip->forceGetBW();
1798}
1799
1800void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1801 path->reset();
1802
1803 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1804 if (rgn.isEmpty()) {
1805 return;
1806 }
1807 (void)rgn.getBoundaryPath(path);
1808}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809
reed@google.com9c135db2014-03-12 18:28:35 +00001810GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1811 SkBaseDevice* dev = this->getTopDevice();
1812 return dev ? dev->accessRenderTarget() : NULL;
1813}
1814
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001815SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001816 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001817 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818}
1819
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001820GrContext* SkCanvas::getGrContext() {
1821#if SK_SUPPORT_GPU
1822 SkBaseDevice* device = this->getTopDevice();
1823 if (NULL != device) {
1824 GrRenderTarget* renderTarget = device->accessRenderTarget();
1825 if (NULL != renderTarget) {
1826 return renderTarget->getContext();
1827 }
1828 }
1829#endif
1830
1831 return NULL;
1832
1833}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001834
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001835void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1836 const SkPaint& paint) {
1837 if (outer.isEmpty()) {
1838 return;
1839 }
1840 if (inner.isEmpty()) {
1841 this->drawRRect(outer, paint);
1842 return;
1843 }
1844
1845 // We don't have this method (yet), but technically this is what we should
1846 // be able to assert...
1847 // SkASSERT(outer.contains(inner));
1848 //
1849 // For now at least check for containment of bounds
1850 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1851
1852 this->onDrawDRRect(outer, inner, paint);
1853}
1854
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855//////////////////////////////////////////////////////////////////////////////
1856// These are the virtual drawing methods
1857//////////////////////////////////////////////////////////////////////////////
1858
reed@google.com2a981812011-04-14 18:59:28 +00001859void SkCanvas::clear(SkColor color) {
1860 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001861 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001862 while (iter.next()) {
1863 iter.fDevice->clear(color);
1864 }
1865}
1866
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001867void SkCanvas::onDiscard() {
1868 if (NULL != fSurfaceBase) {
1869 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1870 }
1871}
1872
reed@android.com8a1c16f2008-12-17 15:59:43 +00001873void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001874 this->internalDrawPaint(paint);
1875}
1876
1877void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001878 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001879
1880 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001881 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 }
1883
reed@google.com4e2b3d32011-04-07 14:18:59 +00001884 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885}
1886
1887void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1888 const SkPaint& paint) {
1889 if ((long)count <= 0) {
1890 return;
1891 }
1892
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001893 SkRect r, storage;
1894 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001895 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001896 // special-case 2 points (common for drawing a single line)
1897 if (2 == count) {
1898 r.set(pts[0], pts[1]);
1899 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001900 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001901 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001902 bounds = &paint.computeFastStrokeBounds(r, &storage);
1903 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001904 return;
1905 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001906 }
reed@google.coma584aed2012-05-16 14:06:02 +00001907
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908 SkASSERT(pts != NULL);
1909
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001910 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001911
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001913 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914 }
reed@google.com4b226022011-01-11 18:32:13 +00001915
reed@google.com4e2b3d32011-04-07 14:18:59 +00001916 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917}
1918
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001919void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001920 SkRect storage;
1921 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001922 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001923 bounds = &paint.computeFastBounds(r, &storage);
1924 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 return;
1926 }
1927 }
reed@google.com4b226022011-01-11 18:32:13 +00001928
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001929 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930
1931 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001932 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 }
1934
reed@google.com4e2b3d32011-04-07 14:18:59 +00001935 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936}
1937
reed@google.com4ed0fb72012-12-12 20:48:18 +00001938void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001939 SkRect storage;
1940 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001941 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001942 bounds = &paint.computeFastBounds(oval, &storage);
1943 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001944 return;
1945 }
1946 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001947
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001948 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001949
1950 while (iter.next()) {
1951 iter.fDevice->drawOval(iter, oval, looper.paint());
1952 }
1953
1954 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001955}
1956
1957void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001958 SkRect storage;
1959 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001960 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001961 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1962 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001963 return;
1964 }
1965 }
1966
1967 if (rrect.isRect()) {
1968 // call the non-virtual version
1969 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001970 return;
1971 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001972 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001973 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1974 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001975 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001976
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001977 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001978
1979 while (iter.next()) {
1980 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1981 }
1982
1983 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001984}
1985
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001986void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1987 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001988 SkRect storage;
1989 const SkRect* bounds = NULL;
1990 if (paint.canComputeFastBounds()) {
1991 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1992 if (this->quickReject(*bounds)) {
1993 return;
1994 }
1995 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001996
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001997 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001998
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001999 while (iter.next()) {
2000 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2001 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002002
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002003 LOOPER_END
2004}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002005
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00002006void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002007 if (!path.isFinite()) {
2008 return;
2009 }
2010
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002011 SkRect storage;
2012 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002013 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002014 const SkRect& pathBounds = path.getBounds();
2015 bounds = &paint.computeFastBounds(pathBounds, &storage);
2016 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002017 return;
2018 }
2019 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002020
2021 const SkRect& r = path.getBounds();
2022 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002023 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002024 this->internalDrawPaint(paint);
2025 }
2026 return;
2027 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002029 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030
2031 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002032 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 }
2034
reed@google.com4e2b3d32011-04-07 14:18:59 +00002035 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002036}
2037
2038void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2039 const SkPaint* paint) {
2040 SkDEBUGCODE(bitmap.validate();)
2041
reed@google.com3d608122011-11-21 15:16:16 +00002042 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002043 SkRect bounds = {
2044 x, y,
2045 x + SkIntToScalar(bitmap.width()),
2046 y + SkIntToScalar(bitmap.height())
2047 };
2048 if (paint) {
2049 (void)paint->computeFastBounds(bounds, &bounds);
2050 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002051 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002052 return;
2053 }
2054 }
reed@google.com4b226022011-01-11 18:32:13 +00002055
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056 SkMatrix matrix;
2057 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002058 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059}
2060
reed@google.com9987ec32011-09-07 11:57:52 +00002061// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002062void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002063 const SkRect& dst, const SkPaint* paint,
2064 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002065 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066 return;
2067 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002068
reed@google.comea033602012-12-14 13:13:55 +00002069 CHECK_LOCKCOUNT_BALANCE(bitmap);
2070
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002071 SkRect storage;
2072 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002073 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002074 if (paint) {
2075 bounds = &paint->computeFastBounds(dst, &storage);
2076 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002077 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002078 return;
2079 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080 }
reed@google.com3d608122011-11-21 15:16:16 +00002081
reed@google.com33535f32012-09-25 15:37:50 +00002082 SkLazyPaint lazy;
2083 if (NULL == paint) {
2084 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002086
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002087 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002088
reed@google.com33535f32012-09-25 15:37:50 +00002089 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002090 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002091 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002092
reed@google.com33535f32012-09-25 15:37:50 +00002093 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094}
2095
reed@google.com71121732012-09-18 15:14:33 +00002096void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002097 const SkRect& dst, const SkPaint* paint,
2098 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002099 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002100 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002101}
2102
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2104 const SkPaint* paint) {
2105 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002106 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107}
2108
reed@google.com9987ec32011-09-07 11:57:52 +00002109void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2110 const SkIRect& center, const SkRect& dst,
2111 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002112 if (bitmap.drawsNothing()) {
2113 return;
2114 }
reed@google.com3d608122011-11-21 15:16:16 +00002115 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002116 SkRect storage;
2117 const SkRect* bounds = &dst;
2118 if (paint) {
2119 bounds = &paint->computeFastBounds(dst, &storage);
2120 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002121 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002122 return;
2123 }
2124 }
2125
reed@google.com9987ec32011-09-07 11:57:52 +00002126 const int32_t w = bitmap.width();
2127 const int32_t h = bitmap.height();
2128
2129 SkIRect c = center;
2130 // pin center to the bounds of the bitmap
2131 c.fLeft = SkMax32(0, center.fLeft);
2132 c.fTop = SkMax32(0, center.fTop);
2133 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2134 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2135
reed@google.com71121732012-09-18 15:14:33 +00002136 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002137 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002138 };
2139 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002140 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002141 };
reed@google.com9987ec32011-09-07 11:57:52 +00002142 SkScalar dstX[4] = {
2143 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2144 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2145 };
2146 SkScalar dstY[4] = {
2147 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2148 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2149 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002150
reed@google.com9987ec32011-09-07 11:57:52 +00002151 if (dstX[1] > dstX[2]) {
2152 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2153 dstX[2] = dstX[1];
2154 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002155
reed@google.com9987ec32011-09-07 11:57:52 +00002156 if (dstY[1] > dstY[2]) {
2157 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2158 dstY[2] = dstY[1];
2159 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002160
reed@google.com9987ec32011-09-07 11:57:52 +00002161 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002162 SkRect s, d;
2163
reed@google.com9987ec32011-09-07 11:57:52 +00002164 s.fTop = srcY[y];
2165 s.fBottom = srcY[y+1];
2166 d.fTop = dstY[y];
2167 d.fBottom = dstY[y+1];
2168 for (int x = 0; x < 3; x++) {
2169 s.fLeft = srcX[x];
2170 s.fRight = srcX[x+1];
2171 d.fLeft = dstX[x];
2172 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002173 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002174 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002175 }
2176 }
2177}
2178
2179void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2180 const SkRect& dst, const SkPaint* paint) {
2181 SkDEBUGCODE(bitmap.validate();)
2182
2183 // Need a device entry-point, so gpu can use a mesh
2184 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2185}
2186
reed@google.comf67e4cf2011-03-15 20:56:58 +00002187class SkDeviceFilteredPaint {
2188public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002189 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2190 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002191 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002192 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002193 newPaint->setFlags(flags.fFlags);
2194 newPaint->setHinting(flags.fHinting);
2195 fPaint = newPaint;
2196 } else {
2197 fPaint = &paint;
2198 }
2199 }
2200
reed@google.comf67e4cf2011-03-15 20:56:58 +00002201 const SkPaint& paint() const { return *fPaint; }
2202
2203private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002204 const SkPaint* fPaint;
2205 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002206};
2207
bungeman@google.com52c748b2011-08-22 21:30:43 +00002208void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2209 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002210 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002211 draw.fDevice->drawRect(draw, r, paint);
2212 } else {
2213 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002214 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002215 draw.fDevice->drawRect(draw, r, p);
2216 }
2217}
2218
2219void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2220 const char text[], size_t byteLength,
2221 SkScalar x, SkScalar y) {
2222 SkASSERT(byteLength == 0 || text != NULL);
2223
2224 // nothing to draw
2225 if (text == NULL || byteLength == 0 ||
2226 draw.fClip->isEmpty() ||
2227 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2228 return;
2229 }
2230
2231 SkScalar width = 0;
2232 SkPoint start;
2233
2234 start.set(0, 0); // to avoid warning
2235 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2236 SkPaint::kStrikeThruText_Flag)) {
2237 width = paint.measureText(text, byteLength);
2238
2239 SkScalar offsetX = 0;
2240 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2241 offsetX = SkScalarHalf(width);
2242 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2243 offsetX = width;
2244 }
2245 start.set(x - offsetX, y);
2246 }
2247
2248 if (0 == width) {
2249 return;
2250 }
2251
2252 uint32_t flags = paint.getFlags();
2253
2254 if (flags & (SkPaint::kUnderlineText_Flag |
2255 SkPaint::kStrikeThruText_Flag)) {
2256 SkScalar textSize = paint.getTextSize();
2257 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2258 SkRect r;
2259
2260 r.fLeft = start.fX;
2261 r.fRight = start.fX + width;
2262
2263 if (flags & SkPaint::kUnderlineText_Flag) {
2264 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2265 start.fY);
2266 r.fTop = offset;
2267 r.fBottom = offset + height;
2268 DrawRect(draw, paint, r, textSize);
2269 }
2270 if (flags & SkPaint::kStrikeThruText_Flag) {
2271 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2272 start.fY);
2273 r.fTop = offset;
2274 r.fBottom = offset + height;
2275 DrawRect(draw, paint, r, textSize);
2276 }
2277 }
2278}
2279
reed@google.come0d9ce82014-04-23 04:00:17 +00002280void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2281 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002282 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283
2284 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002285 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002286 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002287 DrawTextDecorations(iter, dfp.paint(),
2288 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002289 }
2290
reed@google.com4e2b3d32011-04-07 14:18:59 +00002291 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292}
2293
reed@google.come0d9ce82014-04-23 04:00:17 +00002294void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2295 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002296 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002297
reed@android.com8a1c16f2008-12-17 15:59:43 +00002298 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002299 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002301 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002303
reed@google.com4e2b3d32011-04-07 14:18:59 +00002304 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305}
2306
reed@google.come0d9ce82014-04-23 04:00:17 +00002307void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2308 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002309 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002310
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002312 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002314 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002315 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002316
reed@google.com4e2b3d32011-04-07 14:18:59 +00002317 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002318}
2319
reed@google.come0d9ce82014-04-23 04:00:17 +00002320void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2321 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002322 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002323
reed@android.com8a1c16f2008-12-17 15:59:43 +00002324 while (iter.next()) {
2325 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002326 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002327 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002328
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002329 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002330}
2331
reed@google.come0d9ce82014-04-23 04:00:17 +00002332// These will become non-virtual, so they always call the (virtual) onDraw... method
2333void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2334 const SkPaint& paint) {
2335 this->onDrawText(text, byteLength, x, y, paint);
2336}
2337void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2338 const SkPaint& paint) {
2339 this->onDrawPosText(text, byteLength, pos, paint);
2340}
2341void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2342 SkScalar constY, const SkPaint& paint) {
2343 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2344}
2345void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2346 const SkMatrix* matrix, const SkPaint& paint) {
2347 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2348}
2349
reed@android.com8a1c16f2008-12-17 15:59:43 +00002350void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2351 const SkPoint verts[], const SkPoint texs[],
2352 const SkColor colors[], SkXfermode* xmode,
2353 const uint16_t indices[], int indexCount,
2354 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002355 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002356
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 while (iter.next()) {
2358 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002359 colors, xmode, indices, indexCount,
2360 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002361 }
reed@google.com4b226022011-01-11 18:32:13 +00002362
reed@google.com4e2b3d32011-04-07 14:18:59 +00002363 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364}
2365
2366//////////////////////////////////////////////////////////////////////////////
2367// These methods are NOT virtual, and therefore must call back into virtual
2368// methods, rather than actually drawing themselves.
2369//////////////////////////////////////////////////////////////////////////////
2370
2371void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002372 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373 SkPaint paint;
2374
2375 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002376 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002377 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002378 }
2379 this->drawPaint(paint);
2380}
2381
reed@android.com845fdac2009-06-23 03:01:32 +00002382void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383 SkPaint paint;
2384
2385 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002386 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002387 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 }
2389 this->drawPaint(paint);
2390}
2391
2392void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2393 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002394
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395 pt.set(x, y);
2396 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2397}
2398
2399void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2400 SkPoint pt;
2401 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002402
reed@android.com8a1c16f2008-12-17 15:59:43 +00002403 pt.set(x, y);
2404 paint.setColor(color);
2405 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2406}
2407
2408void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2409 const SkPaint& paint) {
2410 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002411
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412 pts[0].set(x0, y0);
2413 pts[1].set(x1, y1);
2414 this->drawPoints(kLines_PointMode, 2, pts, paint);
2415}
2416
2417void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2418 SkScalar right, SkScalar bottom,
2419 const SkPaint& paint) {
2420 SkRect r;
2421
2422 r.set(left, top, right, bottom);
2423 this->drawRect(r, paint);
2424}
2425
2426void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2427 const SkPaint& paint) {
2428 if (radius < 0) {
2429 radius = 0;
2430 }
2431
2432 SkRect r;
2433 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002434 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435}
2436
2437void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2438 const SkPaint& paint) {
2439 if (rx > 0 && ry > 0) {
2440 if (paint.canComputeFastBounds()) {
2441 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002442 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002443 return;
2444 }
2445 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002446 SkRRect rrect;
2447 rrect.setRectXY(r, rx, ry);
2448 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002449 } else {
2450 this->drawRect(r, paint);
2451 }
2452}
2453
reed@android.com8a1c16f2008-12-17 15:59:43 +00002454void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2455 SkScalar sweepAngle, bool useCenter,
2456 const SkPaint& paint) {
2457 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2458 this->drawOval(oval, paint);
2459 } else {
2460 SkPath path;
2461 if (useCenter) {
2462 path.moveTo(oval.centerX(), oval.centerY());
2463 }
2464 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2465 if (useCenter) {
2466 path.close();
2467 }
2468 this->drawPath(path, paint);
2469 }
2470}
2471
2472void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2473 const SkPath& path, SkScalar hOffset,
2474 SkScalar vOffset, const SkPaint& paint) {
2475 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002476
reed@android.com8a1c16f2008-12-17 15:59:43 +00002477 matrix.setTranslate(hOffset, vOffset);
2478 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2479}
2480
reed@android.comf76bacf2009-05-13 14:00:33 +00002481///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002482void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002483 SkBaseDevice* device = this->getDevice();
2484 if (NULL != device) {
2485 device->EXPERIMENTAL_optimize(picture);
2486 }
2487}
reed@android.comf76bacf2009-05-13 14:00:33 +00002488
robertphillips9b14f262014-06-04 05:40:44 -07002489void SkCanvas::EXPERIMENTAL_purge(const SkPicture* picture) {
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002490 SkBaseDevice* device = this->getTopDevice();
2491 if (NULL != device) {
2492 device->EXPERIMENTAL_purge(picture);
2493 }
2494}
2495
robertphillips9b14f262014-06-04 05:40:44 -07002496void SkCanvas::drawPicture(const SkPicture* picture) {
2497 if (NULL != picture) {
2498 this->onDrawPicture(picture);
2499 }
2500}
2501
2502void SkCanvas::onDrawPicture(const SkPicture* picture) {
2503 SkASSERT(NULL != picture);
2504
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002505 SkBaseDevice* device = this->getTopDevice();
2506 if (NULL != device) {
2507 // Canvas has to first give the device the opportunity to render
2508 // the picture itself.
robertphillips9b14f262014-06-04 05:40:44 -07002509 if (device->EXPERIMENTAL_drawPicture(this, picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002510 return; // the device has rendered the entire picture
2511 }
2512 }
2513
robertphillips9b14f262014-06-04 05:40:44 -07002514 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002515}
2516
reed@android.com8a1c16f2008-12-17 15:59:43 +00002517///////////////////////////////////////////////////////////////////////////////
2518///////////////////////////////////////////////////////////////////////////////
2519
2520SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002521 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002522
2523 SkASSERT(canvas);
2524
2525 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2526 fDone = !fImpl->next();
2527}
2528
2529SkCanvas::LayerIter::~LayerIter() {
2530 fImpl->~SkDrawIter();
2531}
2532
2533void SkCanvas::LayerIter::next() {
2534 fDone = !fImpl->next();
2535}
2536
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002537SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002538 return fImpl->getDevice();
2539}
2540
2541const SkMatrix& SkCanvas::LayerIter::matrix() const {
2542 return fImpl->getMatrix();
2543}
2544
2545const SkPaint& SkCanvas::LayerIter::paint() const {
2546 const SkPaint* paint = fImpl->getPaint();
2547 if (NULL == paint) {
2548 paint = &fDefaultPaint;
2549 }
2550 return *paint;
2551}
2552
2553const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2554int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2555int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002556
2557///////////////////////////////////////////////////////////////////////////////
2558
2559SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002560
2561///////////////////////////////////////////////////////////////////////////////
2562
2563static bool supported_for_raster_canvas(const SkImageInfo& info) {
2564 switch (info.alphaType()) {
2565 case kPremul_SkAlphaType:
2566 case kOpaque_SkAlphaType:
2567 break;
2568 default:
2569 return false;
2570 }
2571
2572 switch (info.colorType()) {
2573 case kAlpha_8_SkColorType:
2574 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002575 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002576 break;
2577 default:
2578 return false;
2579 }
2580
2581 return true;
2582}
2583
2584SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2585 if (!supported_for_raster_canvas(info)) {
2586 return NULL;
2587 }
2588
2589 SkBitmap bitmap;
2590 if (!bitmap.allocPixels(info)) {
2591 return NULL;
2592 }
2593
2594 // should this functionality be moved into allocPixels()?
2595 if (!bitmap.info().isOpaque()) {
2596 bitmap.eraseColor(0);
2597 }
2598 return SkNEW_ARGS(SkCanvas, (bitmap));
2599}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002600
2601SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2602 if (!supported_for_raster_canvas(info)) {
2603 return NULL;
2604 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002605
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002606 SkBitmap bitmap;
2607 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2608 return NULL;
2609 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002610 return SkNEW_ARGS(SkCanvas, (bitmap));
2611}