blob: c062895bf0880a9ba3506b8540e3394579f2718c [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
8#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00009#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000010#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkDraw.h"
12#include "SkDrawFilter.h"
13#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000014#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000015#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000017#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000018#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000019#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000020#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000022#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000023#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000026#if SK_SUPPORT_GPU
27#include "GrRenderTarget.h"
28#endif
29
reed@google.comda17f752012-08-16 18:27:05 +000030// experimental for faster tiled drawing...
31//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000032
reed@android.com8a1c16f2008-12-17 15:59:43 +000033//#define SK_TRACE_SAVERESTORE
34
35#ifdef SK_TRACE_SAVERESTORE
36 static int gLayerCounter;
37 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
38 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
39
40 static int gRecCounter;
41 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
42 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
43
44 static int gCanvasCounter;
45 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
46 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
47#else
48 #define inc_layer()
49 #define dec_layer()
50 #define inc_rec()
51 #define dec_rec()
52 #define inc_canvas()
53 #define dec_canvas()
54#endif
55
reed@google.comea033602012-12-14 13:13:55 +000056#ifdef SK_DEBUG
57#include "SkPixelRef.h"
58
reed@google.comf53d0a92013-01-30 13:17:32 +000059/*
60 * Some pixelref subclasses can support being "locked" from another thread
61 * during the lock-scope of skia calling them. In these instances, this balance
62 * check will fail, but may not be indicative of a problem, so we allow a build
63 * flag to disable this check.
64 *
65 * Potentially another fix would be to have a (debug-only) virtual or flag on
66 * pixelref, which could tell us at runtime if this check is valid. That would
67 * eliminate the need for this heavy-handed build check.
68 */
69#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
70class AutoCheckLockCountBalance {
71public:
72 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
73};
74#else
reed@google.comea033602012-12-14 13:13:55 +000075class AutoCheckLockCountBalance {
76public:
77 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
78 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
79 }
80 ~AutoCheckLockCountBalance() {
81 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
82 SkASSERT(count == fLockCount);
83 }
84
85private:
86 const SkPixelRef* fPixelRef;
87 int fLockCount;
88};
reed@google.comf53d0a92013-01-30 13:17:32 +000089#endif
reed@google.comea033602012-12-14 13:13:55 +000090
reed@google.comea033602012-12-14 13:13:55 +000091#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000092
93#else
94 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000095#endif
96
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000097typedef SkTLazy<SkPaint> SkLazyPaint;
98
reed@google.com97af1a62012-08-28 12:19:02 +000099void SkCanvas::predrawNotify() {
100 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000101 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000102 }
103}
104
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000107/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 The clip/matrix/proc are fields that reflect the top of the save/restore
109 stack. Whenever the canvas changes, it marks a dirty flag, and then before
110 these are used (assuming we're not on a layer) we rebuild these cache
111 values: they reflect the top of the save stack, but translated and clipped
112 by the device's XY offset and bitmap-bounds.
113*/
114struct DeviceCM {
115 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000116 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000117 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000119 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000121 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 : fNext(NULL) {
123 if (NULL != device) {
124 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000125 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 }
reed@google.com4b226022011-01-11 18:32:13 +0000127 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000129 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000131 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000133 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 fDevice->unref();
135 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000136 SkDELETE(fPaint);
137 }
reed@google.com4b226022011-01-11 18:32:13 +0000138
reed@google.com045e62d2011-10-24 12:19:46 +0000139 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
140 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000141 int x = fDevice->getOrigin().x();
142 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 int width = fDevice->width();
144 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000145
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 if ((x | y) == 0) {
147 fMatrix = &totalMatrix;
148 fClip = totalClip;
149 } else {
150 fMatrixStorage = totalMatrix;
151 fMatrixStorage.postTranslate(SkIntToScalar(-x),
152 SkIntToScalar(-y));
153 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000154
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 totalClip.translate(-x, -y, &fClip);
156 }
157
reed@google.com045e62d2011-10-24 12:19:46 +0000158 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159
160 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000161
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000163 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 SkRegion::kDifference_Op);
165 }
reed@google.com4b226022011-01-11 18:32:13 +0000166
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000167 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
168
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169#ifdef SK_DEBUG
170 if (!fClip.isEmpty()) {
171 SkIRect deviceR;
172 deviceR.set(0, 0, width, height);
173 SkASSERT(deviceR.contains(fClip.getBounds()));
174 }
175#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000176 }
177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000179 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180};
181
182/* This is the record we keep for each save/restore level in the stack.
183 Since a level optionally copies the matrix and/or stack, we have pointers
184 for these fields. If the value is copied for this level, the copy is
185 stored in the ...Storage field, and the pointer points to that. If the
186 value is not copied for this level, we ignore ...Storage, and just point
187 at the corresponding value in the previous level in the stack.
188*/
189class SkCanvas::MCRec {
190public:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000191 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000192 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
193 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
194 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000195
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 DeviceCM* fLayer;
197 /* If there are any layers in the stack, this points to the top-most
198 one that is at or below this level in the stack (so we know what
199 bitmap/device to draw into from this level. This value is NOT
200 reference counted, since the real owner is either our fLayer field,
201 or a previous one in a lower level.)
202 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000203 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000205 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 if (NULL != prev) {
207 if (flags & SkCanvas::kMatrix_SaveFlag) {
208 fMatrixStorage = *prev->fMatrix;
209 fMatrix = &fMatrixStorage;
210 } else {
211 fMatrix = prev->fMatrix;
212 }
reed@google.com4b226022011-01-11 18:32:13 +0000213
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000215 fRasterClipStorage = *prev->fRasterClip;
216 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 } else {
reed@google.com00177082011-10-12 14:34:30 +0000218 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 }
220
221 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000222 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223
224 fTopLayer = prev->fTopLayer;
225 } else { // no prev
226 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000227
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000229 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 fFilter = NULL;
231 fTopLayer = NULL;
232 }
233 fLayer = NULL;
234
235 // don't bother initializing fNext
236 inc_rec();
237 }
238 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000239 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 SkDELETE(fLayer);
241 dec_rec();
242 }
reed@google.com4b226022011-01-11 18:32:13 +0000243
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244private:
reed@google.com00177082011-10-12 14:34:30 +0000245 SkMatrix fMatrixStorage;
246 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247};
248
249class SkDrawIter : public SkDraw {
250public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000251 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000252 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000253 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 canvas->updateDeviceCMCache();
255
reed@google.com90c07ea2012-04-13 13:50:27 +0000256 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000258 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 }
reed@google.com4b226022011-01-11 18:32:13 +0000260
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 bool next() {
262 // skip over recs with empty clips
263 if (fSkipEmptyClips) {
264 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
265 fCurrLayer = fCurrLayer->fNext;
266 }
267 }
268
reed@google.comf68c5e22012-02-24 16:38:58 +0000269 const DeviceCM* rec = fCurrLayer;
270 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271
272 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000273 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
274 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 fDevice = rec->fDevice;
276 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000278 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279
280 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 return true;
284 }
285 return false;
286 }
reed@google.com4b226022011-01-11 18:32:13 +0000287
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000288 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000289 int getX() const { return fDevice->getOrigin().x(); }
290 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 const SkMatrix& getMatrix() const { return *fMatrix; }
292 const SkRegion& getClip() const { return *fClip; }
293 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295private:
296 SkCanvas* fCanvas;
297 const DeviceCM* fCurrLayer;
298 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 SkBool8 fSkipEmptyClips;
300
301 typedef SkDraw INHERITED;
302};
303
304/////////////////////////////////////////////////////////////////////////////
305
306class AutoDrawLooper {
307public:
reed@google.com8926b162012-03-23 15:36:36 +0000308 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000309 bool skipLayerForImageFilter = false,
310 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000311 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 fPaint = NULL;
314 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000315 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317
reed@google.com8926b162012-03-23 15:36:36 +0000318 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
319 SkPaint tmp;
320 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000321 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
322 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000323 // we'll clear the imageFilter for the actual draws in next(), so
324 // it will only be applied during the restore().
325 fDoClearImageFilter = true;
326 }
327
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000328 if (SkDrawLooper* looper = paint.getLooper()) {
329 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
330 looper->contextSize());
331 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000332 fIsSimple = false;
333 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000334 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000335 // can we be marked as simple?
336 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000337 }
338 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000341 if (fDoClearImageFilter) {
342 fCanvas->internalRestore();
343 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000344 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000346
reed@google.com4e2b3d32011-04-07 14:18:59 +0000347 const SkPaint& paint() const {
348 SkASSERT(fPaint);
349 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000351
reed@google.com129ec222012-05-15 13:24:09 +0000352 bool next(SkDrawFilter::Type drawType) {
353 if (fDone) {
354 return false;
355 } else if (fIsSimple) {
356 fDone = true;
357 fPaint = &fOrigPaint;
358 return !fPaint->nothingToDraw();
359 } else {
360 return this->doNext(drawType);
361 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000362 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000365 SkLazyPaint fLazyPaint;
366 SkCanvas* fCanvas;
367 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000368 SkDrawFilter* fFilter;
369 const SkPaint* fPaint;
370 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000371 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000372 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000373 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000374 SkDrawLooper::Context* fLooperContext;
375 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000376
377 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378};
379
reed@google.com129ec222012-05-15 13:24:09 +0000380bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000381 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000382 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000383 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000384
385 SkPaint* paint = fLazyPaint.set(fOrigPaint);
386
387 if (fDoClearImageFilter) {
388 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000389 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000391 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000392 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000393 return false;
394 }
395 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000396 if (!fFilter->filter(paint, drawType)) {
397 fDone = true;
398 return false;
399 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000400 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000401 // no looper means we only draw once
402 fDone = true;
403 }
404 }
405 fPaint = paint;
406
407 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000408 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000409 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000410 }
411
412 // call this after any possible paint modifiers
413 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000414 fPaint = NULL;
415 return false;
416 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000417 return true;
418}
419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420#include "SkColorPriv.h"
421
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422////////// macros to place around the internal draw calls //////////////////
423
reed@google.com8926b162012-03-23 15:36:36 +0000424#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000425 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000426 AutoDrawLooper looper(this, paint, true); \
427 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000428 SkDrawIter iter(this);
429
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000430#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000431 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000432 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000435
reed@google.com4e2b3d32011-04-07 14:18:59 +0000436#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437
438////////////////////////////////////////////////////////////////////////////
439
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000440SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000441 fCachedLocalClipBounds.setEmpty();
442 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000443 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000444 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000445 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000446 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000447 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000448 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449
450 fMCRec = (MCRec*)fMCStack.push_back();
451 new (fMCRec) MCRec(NULL, 0);
452
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000453 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455
reed@google.com97af1a62012-08-28 12:19:02 +0000456 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000457
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000458 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459}
460
reed@google.comcde92112011-07-06 20:00:52 +0000461SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000462 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
463{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000464 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000465
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000466 this->init(NULL);
467}
468
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000469SkCanvas::SkCanvas(int width, int height)
470 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
471{
472 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000473
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000474 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000475 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000476 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
477}
478
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000479SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000480 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
481{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 inc_canvas();
483
484 this->init(device);
485}
486
487SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000488 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
489{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 inc_canvas();
491
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000492 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493}
494
495SkCanvas::~SkCanvas() {
496 // free up the contents of our deque
497 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000498 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 this->internalRestore(); // restore the last, since we're going away
501
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000502 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 dec_canvas();
505}
506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507SkDrawFilter* SkCanvas::getDrawFilter() const {
508 return fMCRec->fFilter;
509}
510
511SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
512 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
513 return filter;
514}
515
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000516SkMetaData& SkCanvas::getMetaData() {
517 // metadata users are rare, so we lazily allocate it. If that changes we
518 // can decide to just make it a field in the device (rather than a ptr)
519 if (NULL == fMetaData) {
520 fMetaData = new SkMetaData;
521 }
522 return *fMetaData;
523}
524
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525///////////////////////////////////////////////////////////////////////////////
526
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000527void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000528 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000529 if (device) {
530 device->flush();
531 }
532}
533
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000534SkISize SkCanvas::getTopLayerSize() const {
535 SkBaseDevice* d = this->getTopDevice();
536 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
537}
538
539SkIPoint SkCanvas::getTopLayerOrigin() const {
540 SkBaseDevice* d = this->getTopDevice();
541 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
542}
543
544SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000545 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000546 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
547}
548
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000549SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000551 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 SkASSERT(rec && rec->fLayer);
553 return rec->fLayer->fDevice;
554}
555
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000556SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000557 if (updateMatrixClip) {
558 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
559 }
reed@google.com9266fed2011-03-30 00:18:03 +0000560 return fMCRec->fTopLayer->fDevice;
561}
562
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000563SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000565 SkDeque::F2BIter iter(fMCStack);
566 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000568 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569
570 if (rootDevice == device) {
571 return device;
572 }
reed@google.com4b226022011-01-11 18:32:13 +0000573
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000575 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 }
577 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000578 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 }
580
581 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
582 rootDevice = device;
583
584 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 /* Now we update our initial region to have the bounds of the new device,
587 and then intersect all of the clips in our stack with these bounds,
588 to ensure that we can't draw outside of the device's bounds (and trash
589 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000590
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 NOTE: this is only a partial-fix, since if the new device is larger than
592 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000593 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
595 reconstruct the correct clips, so this approximation will have to do.
596 The caller really needs to restore() back to the base if they want to
597 accurately take advantage of the new device bounds.
598 */
599
reed@google.com42aea282012-03-28 16:19:15 +0000600 SkIRect bounds;
601 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000603 } else {
604 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 }
reed@google.com42aea282012-03-28 16:19:15 +0000606 // now jam our 1st clip to be bounds, and intersect the rest with that
607 rec->fRasterClip->setRect(bounds);
608 while ((rec = (MCRec*)iter.next()) != NULL) {
609 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
610 }
611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 return device;
613}
614
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000615bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
616 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
617 return false;
618 }
619
620 bool weAllocated = false;
621 if (NULL == bitmap->pixelRef()) {
622 if (!bitmap->allocPixels()) {
623 return false;
624 }
625 weAllocated = true;
626 }
627
628 SkBitmap bm(*bitmap);
629 bm.lockPixels();
630 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
631 return true;
632 }
633
634 if (weAllocated) {
635 bitmap->setPixelRef(NULL);
636 }
637 return false;
638}
reed@google.com51df9e32010-12-23 19:29:18 +0000639
bsalomon@google.comc6980972011-11-02 19:57:21 +0000640bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000641 SkIRect r = srcRect;
642 const SkISize size = this->getBaseLayerSize();
643 if (!r.intersect(0, 0, size.width(), size.height())) {
644 bitmap->reset();
645 return false;
646 }
647
648 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
649 // bitmap will already be reset.
650 return false;
651 }
652 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
653 bitmap->reset();
654 return false;
655 }
656 return true;
657}
658
659bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
660 switch (origInfo.colorType()) {
661 case kUnknown_SkColorType:
662 case kIndex_8_SkColorType:
663 return false;
664 default:
665 break;
666 }
667 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
668 return false;
669 }
670 if (0 == origInfo.width() || 0 == origInfo.height()) {
671 return false;
672 }
673
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000674 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000675 if (!device) {
676 return false;
677 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000678
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000679 const SkISize size = this->getBaseLayerSize();
680 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
681 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000682 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000683 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000684
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000685 SkImageInfo info = origInfo;
686 // the intersect may have shrunk info's logical size
687 info.fWidth = srcR.width();
688 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000689
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000690 // if x or y are negative, then we have to adjust pixels
691 if (x > 0) {
692 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000693 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000694 if (y > 0) {
695 y = 0;
696 }
697 // here x,y are either 0 or negative
698 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000699
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000700 // The device can assert that the requested area is always contained in its bounds
701 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000702}
703
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000704bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
705 if (bitmap.getTexture()) {
706 return false;
707 }
708 SkBitmap bm(bitmap);
709 bm.lockPixels();
710 if (bm.getPixels()) {
711 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
712 }
713 return false;
714}
715
716bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
717 int x, int y) {
718 switch (origInfo.colorType()) {
719 case kUnknown_SkColorType:
720 case kIndex_8_SkColorType:
721 return false;
722 default:
723 break;
724 }
725 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
726 return false;
727 }
728
729 const SkISize size = this->getBaseLayerSize();
730 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
731 if (!target.intersect(0, 0, size.width(), size.height())) {
732 return false;
733 }
734
735 SkBaseDevice* device = this->getDevice();
736 if (!device) {
737 return false;
738 }
739
740 SkImageInfo info = origInfo;
741 // the intersect may have shrunk info's logical size
742 info.fWidth = target.width();
743 info.fHeight = target.height();
744
745 // if x or y are negative, then we have to adjust pixels
746 if (x > 0) {
747 x = 0;
748 }
749 if (y > 0) {
750 y = 0;
751 }
752 // here x,y are either 0 or negative
753 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
754
755 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000756 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000757}
reed@google.com51df9e32010-12-23 19:29:18 +0000758
junov@google.com4370aed2012-01-18 16:21:08 +0000759SkCanvas* SkCanvas::canvasForDrawIter() {
760 return this;
761}
762
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763//////////////////////////////////////////////////////////////////////////////
764
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765void SkCanvas::updateDeviceCMCache() {
766 if (fDeviceCMDirty) {
767 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000768 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000770
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000772 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000774 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000776 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 } while ((layer = layer->fNext) != NULL);
778 }
779 fDeviceCMDirty = false;
780 }
781}
782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783///////////////////////////////////////////////////////////////////////////////
784
785int SkCanvas::internalSave(SaveFlags flags) {
786 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 MCRec* newTop = (MCRec*)fMCStack.push_back();
789 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000792
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000793 if (SkCanvas::kClip_SaveFlag & flags) {
794 fClipStack.save();
795 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000796
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 return saveCount;
798}
799
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000800int SkCanvas::save() {
801 this->willSave(kMatrixClip_SaveFlag);
802 return this->internalSave(kMatrixClip_SaveFlag);
803}
804
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000806 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 // call shared impl
808 return this->internalSave(flags);
809}
810
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000812#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000814#else
815 return true;
816#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817}
818
junov@chromium.orga907ac32012-02-24 21:54:07 +0000819bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000820 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000821 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000822 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000823 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000824 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000825 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000826
827 if (imageFilter) {
828 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
829 // Filters may grow the bounds beyond the device bounds.
830 op = SkRegion::kReplace_Op;
831 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000832 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 if (NULL != bounds) {
834 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000835
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 this->getTotalMatrix().mapRect(&r, *bounds);
837 r.roundOut(&ir);
838 // early exit if the layer's bounds are clipped out
839 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000840 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000841 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000842 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000843 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 }
845 } else { // no user bounds, so just use the clip
846 ir = clipBounds;
847 }
848
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000849 if (bounds_affects_clip(flags)) {
850 fClipStack.clipDevRect(ir, op);
851 // early exit if the clip is now empty
852 if (!fMCRec->fRasterClip->op(ir, op)) {
853 return false;
854 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 }
856
857 if (intersection) {
858 *intersection = ir;
859 }
860 return true;
861}
862
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000863int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
864 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
865 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
866}
867
junov@chromium.orga907ac32012-02-24 21:54:07 +0000868int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
869 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000870 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
871 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000872}
873
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000874static SkBaseDevice* create_compatible_device(SkCanvas* canvas,
875 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000876 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000877 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000878}
879
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000880int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
881 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000882#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000883 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000884#endif
885
junov@chromium.orga907ac32012-02-24 21:54:07 +0000886 // do this before we create the layer. We don't call the public save() since
887 // that would invoke a possibly overridden virtual
888 int count = this->internalSave(flags);
889
890 fDeviceCMDirty = true;
891
892 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000893 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 return count;
895 }
896
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000897 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
898 // the clipRectBounds() call above?
899 if (kNoLayer_SaveLayerStrategy == strategy) {
900 return count;
901 }
902
reed@google.comb55deeb2012-01-06 14:43:09 +0000903 // Kill the imagefilter if our device doesn't allow it
904 SkLazyPaint lazyP;
905 if (paint && paint->getImageFilter()) {
906 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000907 if (justForImageFilter) {
908 // early exit if the layer was just for the imageFilter
909 return count;
910 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000911 SkPaint* p = lazyP.set(*paint);
912 p->setImageFilter(NULL);
913 paint = p;
914 }
915 }
916
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000917 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
918 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
919 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000921 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000922 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000923 device = create_compatible_device(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000924 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000925 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000926 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000927 if (NULL == device) {
928 SkDebugf("Unable to create device for layer.");
929 return count;
930 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000931
reed@google.com6f8f2922011-03-04 22:27:10 +0000932 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000933 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 device->unref();
935
936 layer->fNext = fMCRec->fTopLayer;
937 fMCRec->fLayer = layer;
938 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
939
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000940 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 return count;
942}
943
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000944int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
945 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
946}
947
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
949 SaveFlags flags) {
950 if (0xFF == alpha) {
951 return this->saveLayer(bounds, NULL, flags);
952 } else {
953 SkPaint tmpPaint;
954 tmpPaint.setAlpha(alpha);
955 return this->saveLayer(bounds, &tmpPaint, flags);
956 }
957}
958
959void SkCanvas::restore() {
960 // check for underflow
961 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000962 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 this->internalRestore();
964 }
965}
966
967void SkCanvas::internalRestore() {
968 SkASSERT(fMCStack.count() != 0);
969
970 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000971 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000973 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
974 fClipStack.restore();
975 }
976
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000977 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 DeviceCM* layer = fMCRec->fLayer; // may be null
979 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
980 fMCRec->fLayer = NULL;
981
982 // now do the normal restore()
983 fMCRec->~MCRec(); // balanced in save()
984 fMCStack.pop_back();
985 fMCRec = (MCRec*)fMCStack.back();
986
987 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
988 since if we're being recorded, we don't want to record this (the
989 recorder will have already recorded the restore).
990 */
991 if (NULL != layer) {
992 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000993 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000994 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
995 layer->fPaint);
996 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000998
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000999 SkASSERT(fSaveLayerCount > 0);
1000 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001 }
1002 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001003 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004}
1005
1006int SkCanvas::getSaveCount() const {
1007 return fMCStack.count();
1008}
1009
1010void SkCanvas::restoreToCount(int count) {
1011 // sanity check
1012 if (count < 1) {
1013 count = 1;
1014 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001015
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001016 int n = this->getSaveCount() - count;
1017 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 this->restore();
1019 }
1020}
1021
reed@google.com7c202932011-12-14 18:48:05 +00001022bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001023 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001024}
1025
reed@google.com76f10a32014-02-05 15:32:21 +00001026SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1027 return this->onNewSurface(info);
1028}
1029
1030SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1031 SkBaseDevice* dev = this->getDevice();
1032 return dev ? dev->newSurface(info) : NULL;
1033}
1034
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001035SkImageInfo SkCanvas::imageInfo() const {
1036 SkBaseDevice* dev = this->getDevice();
1037 if (dev) {
1038 return dev->imageInfo();
1039 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001040 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001041 }
1042}
1043
1044const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1045 return this->onPeekPixels(info, rowBytes);
1046}
1047
1048const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1049 SkBaseDevice* dev = this->getDevice();
1050 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1051}
1052
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001053void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1054 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1055 if (pixels && origin) {
1056 *origin = this->getTopDevice(false)->getOrigin();
1057 }
1058 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001059}
1060
1061void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1062 SkBaseDevice* dev = this->getTopDevice();
1063 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1064}
1065
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001066SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1067 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1068 if (NULL == fAddr) {
1069 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001070 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001071 return; // failure, fAddr is NULL
1072 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001073 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1074 return; // failure, fAddr is NULL
1075 }
1076 fAddr = fBitmap.getPixels();
1077 fRowBytes = fBitmap.rowBytes();
1078 }
1079 SkASSERT(fAddr); // success
1080}
1081
1082bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1083 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001084 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001085 } else {
1086 bitmap->reset();
1087 return false;
1088 }
1089}
1090
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001091void SkCanvas::onPushCull(const SkRect& cullRect) {
1092 // do nothing. Subclasses may do something
1093}
1094
1095void SkCanvas::onPopCull() {
1096 // do nothing. Subclasses may do something
1097}
1098
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001100#ifdef SK_DEBUG
1101// Ensure that cull rects are monotonically nested in device space.
1102void SkCanvas::validateCull(const SkIRect& devCull) {
1103 if (fCullStack.isEmpty()
1104 || devCull.isEmpty()
1105 || fCullStack.top().contains(devCull)) {
1106 return;
1107 }
1108
1109 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1110 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1111 fCullStack.top().x(), fCullStack.top().y(),
1112 fCullStack.top().right(), fCullStack.top().bottom()));
1113
1114#ifdef ASSERT_NESTED_CULLING
1115 SkDEBUGFAIL("Invalid cull.");
1116#endif
1117}
1118#endif
1119
1120void SkCanvas::pushCull(const SkRect& cullRect) {
1121 ++fCullCount;
1122 this->onPushCull(cullRect);
1123
1124#ifdef SK_DEBUG
1125 // Map the cull rect into device space.
1126 SkRect mappedCull;
1127 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1128
1129 // Take clipping into account.
1130 SkIRect devClip, devCull;
1131 mappedCull.roundOut(&devCull);
1132 this->getClipDeviceBounds(&devClip);
1133 if (!devCull.intersect(devClip)) {
1134 devCull.setEmpty();
1135 }
1136
1137 this->validateCull(devCull);
1138 fCullStack.push(devCull); // balanced in popCull
1139#endif
1140}
1141
1142void SkCanvas::popCull() {
1143 SkASSERT(fCullStack.count() == fCullCount);
1144
1145 if (fCullCount > 0) {
1146 --fCullCount;
1147 this->onPopCull();
1148
1149 SkDEBUGCODE(fCullStack.pop());
1150 }
1151}
1152
1153/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001155void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001157 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 return;
1159 }
1160
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001161 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001163 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001165
1166 SkDEBUGCODE(bitmap.validate();)
1167 CHECK_LOCKCOUNT_BALANCE(bitmap);
1168
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001169 SkRect storage;
1170 const SkRect* bounds = NULL;
1171 if (paint && paint->canComputeFastBounds()) {
1172 bitmap.getBounds(&storage);
1173 matrix.mapRect(&storage);
1174 bounds = &paint->computeFastBounds(storage, &storage);
1175 }
1176
1177 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001178
1179 while (iter.next()) {
1180 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1181 }
1182
1183 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184}
1185
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001186void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001187 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 SkPaint tmp;
1189 if (NULL == paint) {
1190 tmp.setDither(true);
1191 paint = &tmp;
1192 }
reed@google.com4b226022011-01-11 18:32:13 +00001193
reed@google.com8926b162012-03-23 15:36:36 +00001194 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001196 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001197 paint = &looper.paint();
1198 SkImageFilter* filter = paint->getImageFilter();
1199 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001200 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001201 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001202 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001203 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001204 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001205 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001206 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001207 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001208 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1209 SkAutoUnref aur(NULL);
1210 if (!cache) {
1211 cache = SkImageFilter::Cache::Create();
1212 aur.reset(cache);
1213 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001214 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001215 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001216 SkPaint tmpUnfiltered(*paint);
1217 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001218 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1219 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001220 }
1221 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001222 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001223 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001225 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226}
1227
reed@google.com8926b162012-03-23 15:36:36 +00001228void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1229 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001230 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001231 return;
1232 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001233 SkDEBUGCODE(bitmap.validate();)
1234 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001235
reed@google.com8926b162012-03-23 15:36:36 +00001236 SkPaint tmp;
1237 if (NULL == paint) {
1238 paint = &tmp;
1239 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001240
reed@google.com8926b162012-03-23 15:36:36 +00001241 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001242
reed@google.com8926b162012-03-23 15:36:36 +00001243 while (iter.next()) {
1244 paint = &looper.paint();
1245 SkImageFilter* filter = paint->getImageFilter();
1246 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1247 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001248 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001249 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001250 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001251 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001252 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001253 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001254 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1255 SkAutoUnref aur(NULL);
1256 if (!cache) {
1257 cache = SkImageFilter::Cache::Create();
1258 aur.reset(cache);
1259 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001260 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001261 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001262 SkPaint tmpUnfiltered(*paint);
1263 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001264 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001265 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001266 }
1267 } else {
1268 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1269 }
1270 }
1271 LOOPER_END
1272}
1273
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001275void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001276 SkMatrix m;
1277 m.setTranslate(dx, dy);
1278 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279}
1280
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001281void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001282 SkMatrix m;
1283 m.setScale(sx, sy);
1284 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001287void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001288 SkMatrix m;
1289 m.setRotate(degrees);
1290 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291}
1292
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001293void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001294 SkMatrix m;
1295 m.setSkew(sx, sy);
1296 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001297}
1298
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001299void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001300 if (matrix.isIdentity()) {
1301 return;
1302 }
1303
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001305 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001306 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001307
1308 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001309}
1310
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311void SkCanvas::setMatrix(const SkMatrix& matrix) {
1312 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001313 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001315 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316}
1317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318void SkCanvas::resetMatrix() {
1319 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001320
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 matrix.reset();
1322 this->setMatrix(matrix);
1323}
1324
1325//////////////////////////////////////////////////////////////////////////////
1326
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001327void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001328 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1329 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001330}
1331
1332void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001333#ifdef SK_ENABLE_CLIP_QUICKREJECT
1334 if (SkRegion::kIntersect_Op == op) {
1335 if (fMCRec->fRasterClip->isEmpty()) {
1336 return false;
1337 }
1338
reed@google.com3b3e8952012-08-16 20:53:31 +00001339 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001340 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001341 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001342
1343 fClipStack.clipEmpty();
1344 return fMCRec->fRasterClip->setEmpty();
1345 }
1346 }
1347#endif
1348
reed@google.com5c3d1472011-02-22 19:12:23 +00001349 AutoValidateClip avc(this);
1350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001352 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001353 if (!fAllowSoftClip) {
1354 edgeStyle = kHard_ClipEdgeStyle;
1355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356
1357 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001358 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001359 // the matrix. This means we don't have to a) make a path, and b) tell
1360 // the region code to scan-convert the path, only to discover that it
1361 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363
1364 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001365 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1366 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001368 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001369 // and clip against that, since it can handle any matrix. However, to
1370 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1371 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 SkPath path;
1373
1374 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001375 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376 }
1377}
1378
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001379static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1380 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001381 // base is used to limit the size (and therefore memory allocation) of the
1382 // region that results from scan converting devPath.
1383 SkRegion base;
1384
reed@google.com819c9212011-02-23 18:56:55 +00001385 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001386 // since we are intersect, we can do better (tighter) with currRgn's
1387 // bounds, than just using the device. However, if currRgn is complex,
1388 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001389 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001390 // FIXME: we should also be able to do this when currClip->isBW(),
1391 // but relaxing the test above triggers GM asserts in
1392 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001393 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001394 } else {
reed@google.com00177082011-10-12 14:34:30 +00001395 base.setRect(currClip->getBounds());
1396 SkRasterClip clip;
1397 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001399 }
reed@google.com819c9212011-02-23 18:56:55 +00001400 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001401 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001402 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001403 currClip->setEmpty();
1404 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001405 }
1406
junov@chromium.orga907ac32012-02-24 21:54:07 +00001407 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001408
1409 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001410 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001411 } else {
reed@google.com00177082011-10-12 14:34:30 +00001412 SkRasterClip clip;
1413 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001414 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001415 }
1416 }
1417}
1418
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001419void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001420 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001421 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001422 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1423 } else {
1424 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001425 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001426}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001427
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001429 SkRRect transformedRRect;
1430 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1431 AutoValidateClip avc(this);
1432
1433 fDeviceCMDirty = true;
1434 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001435 if (!fAllowSoftClip) {
1436 edgeStyle = kHard_ClipEdgeStyle;
1437 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001438
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001439 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001440
1441 SkPath devPath;
1442 devPath.addRRect(transformedRRect);
1443
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001444 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1445 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001446 }
1447
1448 SkPath path;
1449 path.addRRect(rrect);
1450 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001451 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001452}
1453
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001454void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001455 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1456 SkRect r;
1457 if (!path.isInverseFillType() && path.isRect(&r)) {
1458 this->onClipRect(r, op, edgeStyle);
1459 } else {
1460 this->onClipPath(path, op, edgeStyle);
1461 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001462}
1463
1464void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001465#ifdef SK_ENABLE_CLIP_QUICKREJECT
1466 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1467 if (fMCRec->fRasterClip->isEmpty()) {
1468 return false;
1469 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001470
reed@google.com3b3e8952012-08-16 20:53:31 +00001471 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001472 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001473 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001474
reed@google.comda17f752012-08-16 18:27:05 +00001475 fClipStack.clipEmpty();
1476 return fMCRec->fRasterClip->setEmpty();
1477 }
1478 }
1479#endif
1480
reed@google.com5c3d1472011-02-22 19:12:23 +00001481 AutoValidateClip avc(this);
1482
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001484 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001485 if (!fAllowSoftClip) {
1486 edgeStyle = kHard_ClipEdgeStyle;
1487 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488
1489 SkPath devPath;
1490 path.transform(*fMCRec->fMatrix, &devPath);
1491
reed@google.comfe701122011-11-08 19:41:23 +00001492 // Check if the transfomation, or the original path itself
1493 // made us empty. Note this can also happen if we contained NaN
1494 // values. computing the bounds detects this, and will set our
1495 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1496 if (devPath.getBounds().isEmpty()) {
1497 // resetting the path will remove any NaN or other wanky values
1498 // that might upset our scan converter.
1499 devPath.reset();
1500 }
1501
reed@google.com5c3d1472011-02-22 19:12:23 +00001502 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001503 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001504
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001505 if (fAllowSimplifyClip) {
1506 devPath.reset();
1507 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1508 const SkClipStack* clipStack = getClipStack();
1509 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1510 const SkClipStack::Element* element;
1511 while ((element = iter.next())) {
1512 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001513 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001514 if (type != SkClipStack::Element::kEmpty_Type) {
1515 element->asPath(&operand);
1516 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001517 SkRegion::Op elementOp = element->getOp();
1518 if (elementOp == SkRegion::kReplace_Op) {
1519 devPath = operand;
1520 } else {
1521 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1522 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001523 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1524 // perhaps we need an API change to avoid this sort of mixed-signals about
1525 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526 if (element->isAA()) {
1527 edgeStyle = kSoft_ClipEdgeStyle;
1528 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001529 }
1530 op = SkRegion::kReplace_Op;
1531 }
1532
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001533 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534}
1535
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001536void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001537 bool inverseFilled) {
1538 // This is for updating the clip conservatively using only bounds
1539 // information.
1540 // Contract:
1541 // The current clip must contain the true clip. The true
1542 // clip is the clip that would have normally been computed
1543 // by calls to clipPath and clipRRect
1544 // Objective:
1545 // Keep the current clip as small as possible without
1546 // breaking the contract, using only clip bounding rectangles
1547 // (for performance).
1548
1549 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1550 // don't have to worry about getting caught in a loop. Thus anywhere
1551 // we call a virtual method, we explicitly prefix it with
1552 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001553
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001554 if (inverseFilled) {
1555 switch (op) {
1556 case SkRegion::kIntersect_Op:
1557 case SkRegion::kDifference_Op:
1558 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001559 // the clip unchanged conservatively respects the contract.
1560 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001561 case SkRegion::kUnion_Op:
1562 case SkRegion::kReplace_Op:
1563 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001564 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001565 // These ops can grow the current clip up to the extents of
1566 // the input clip, which is inverse filled, so we just set
1567 // the current clip to the device bounds.
1568 SkRect deviceBounds;
1569 SkIRect deviceIBounds;
1570 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001571 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001572
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001573 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001574 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001575 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001576 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001577 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001578 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 break;
1580 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001581 default:
1582 SkASSERT(0); // unhandled op?
1583 }
1584 } else {
1585 // Not inverse filled
1586 switch (op) {
1587 case SkRegion::kIntersect_Op:
1588 case SkRegion::kUnion_Op:
1589 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001590 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1591 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001592 case SkRegion::kDifference_Op:
1593 // Difference can only shrink the current clip.
1594 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001596 case SkRegion::kReverseDifference_Op:
1597 // To reverse, we swap in the bounds with a replace op.
1598 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001599 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1600 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001601 case SkRegion::kXOR_Op:
1602 // Be conservative, based on (A XOR B) always included in (A union B),
1603 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001604 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1605 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001606 default:
1607 SkASSERT(0); // unhandled op?
1608 }
1609 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001610}
1611
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001612void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001613 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614}
1615
1616void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001617 AutoValidateClip avc(this);
1618
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001620 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621
reed@google.com5c3d1472011-02-22 19:12:23 +00001622 // todo: signal fClipStack that we have a region, and therefore (I guess)
1623 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001624 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001625
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627}
1628
reed@google.com819c9212011-02-23 18:56:55 +00001629#ifdef SK_DEBUG
1630void SkCanvas::validateClip() const {
1631 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001632 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001633 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001634 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001635 return;
1636 }
1637
reed@google.com819c9212011-02-23 18:56:55 +00001638 SkIRect ir;
1639 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001640 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001641
robertphillips@google.com80214e22012-07-20 15:33:18 +00001642 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001643 const SkClipStack::Element* element;
1644 while ((element = iter.next()) != NULL) {
1645 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001646 case SkClipStack::Element::kRect_Type:
1647 element->getRect().round(&ir);
1648 tmpClip.op(ir, element->getOp());
1649 break;
1650 case SkClipStack::Element::kEmpty_Type:
1651 tmpClip.setEmpty();
1652 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001653 default: {
1654 SkPath path;
1655 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001656 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001657 break;
1658 }
reed@google.com819c9212011-02-23 18:56:55 +00001659 }
1660 }
reed@google.com819c9212011-02-23 18:56:55 +00001661}
1662#endif
1663
reed@google.com90c07ea2012-04-13 13:50:27 +00001664void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001665 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001666 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001667
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001668 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001669 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001670 }
1671}
1672
reed@google.com5c3d1472011-02-22 19:12:23 +00001673///////////////////////////////////////////////////////////////////////////////
1674
reed@google.com754de5f2014-02-24 19:38:20 +00001675bool SkCanvas::isClipEmpty() const {
1676 return fMCRec->fRasterClip->isEmpty();
1677}
1678
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001679bool SkCanvas::isClipRect() const {
1680 return fMCRec->fRasterClip->isRect();
1681}
1682
reed@google.com3b3e8952012-08-16 20:53:31 +00001683bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001684
reed@google.com16078632011-12-06 18:56:37 +00001685 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001686 return true;
1687
reed@google.com00177082011-10-12 14:34:30 +00001688 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689 return true;
1690 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691
tomhudson@google.com8d430182011-06-06 19:11:19 +00001692 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001693 SkRect dst;
1694 fMCRec->fMatrix->mapRect(&dst, rect);
1695 SkIRect idst;
1696 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001697 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001698 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001699 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001700
reed@android.coma380ae42009-07-21 01:17:02 +00001701 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001702 // TODO: should we use | instead, or compare all 4 at once?
1703 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001704 return true;
1705 }
reed@google.comc0784db2013-12-13 21:16:12 +00001706 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001707 return true;
1708 }
1709 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711}
1712
reed@google.com3b3e8952012-08-16 20:53:31 +00001713bool SkCanvas::quickReject(const SkPath& path) const {
1714 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715}
1716
reed@google.com3b3e8952012-08-16 20:53:31 +00001717bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001718 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001719 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720 return false;
1721 }
1722
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001723 SkMatrix inverse;
1724 // if we can't invert the CTM, we can't return local clip bounds
1725 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001726 if (bounds) {
1727 bounds->setEmpty();
1728 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001729 return false;
1730 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001731
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001732 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001733 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001734 // adjust it outwards in case we are antialiasing
1735 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001736
reed@google.com8f4d2302013-12-17 16:44:46 +00001737 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1738 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739 inverse.mapRect(bounds, r);
1740 }
1741 return true;
1742}
1743
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001744bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001745 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001746 if (clip.isEmpty()) {
1747 if (bounds) {
1748 bounds->setEmpty();
1749 }
1750 return false;
1751 }
1752
1753 if (NULL != bounds) {
1754 *bounds = clip.getBounds();
1755 }
1756 return true;
1757}
1758
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759const SkMatrix& SkCanvas::getTotalMatrix() const {
1760 return *fMCRec->fMatrix;
1761}
1762
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001763#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001764SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001765 if (fMCRec->fRasterClip->isEmpty()) {
1766 return kEmpty_ClipType;
1767 }
1768 if (fMCRec->fRasterClip->isRect()) {
1769 return kRect_ClipType;
1770 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001771 return kComplex_ClipType;
1772}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001773#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001774
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001775const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1776 return fMCRec->fRasterClip->forceGetBW();
1777}
1778
1779void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1780 path->reset();
1781
1782 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1783 if (rgn.isEmpty()) {
1784 return;
1785 }
1786 (void)rgn.getBoundaryPath(path);
1787}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788
reed@google.com9c135db2014-03-12 18:28:35 +00001789GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1790 SkBaseDevice* dev = this->getTopDevice();
1791 return dev ? dev->accessRenderTarget() : NULL;
1792}
1793
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001794SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001795 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001796 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797}
1798
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001799GrContext* SkCanvas::getGrContext() {
1800#if SK_SUPPORT_GPU
1801 SkBaseDevice* device = this->getTopDevice();
1802 if (NULL != device) {
1803 GrRenderTarget* renderTarget = device->accessRenderTarget();
1804 if (NULL != renderTarget) {
1805 return renderTarget->getContext();
1806 }
1807 }
1808#endif
1809
1810 return NULL;
1811
1812}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001813
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001814void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1815 const SkPaint& paint) {
1816 if (outer.isEmpty()) {
1817 return;
1818 }
1819 if (inner.isEmpty()) {
1820 this->drawRRect(outer, paint);
1821 return;
1822 }
1823
1824 // We don't have this method (yet), but technically this is what we should
1825 // be able to assert...
1826 // SkASSERT(outer.contains(inner));
1827 //
1828 // For now at least check for containment of bounds
1829 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1830
1831 this->onDrawDRRect(outer, inner, paint);
1832}
1833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834//////////////////////////////////////////////////////////////////////////////
1835// These are the virtual drawing methods
1836//////////////////////////////////////////////////////////////////////////////
1837
reed@google.com2a981812011-04-14 18:59:28 +00001838void SkCanvas::clear(SkColor color) {
1839 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001840 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001841 while (iter.next()) {
1842 iter.fDevice->clear(color);
1843 }
1844}
1845
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001846void SkCanvas::onDiscard() {
1847 if (NULL != fSurfaceBase) {
1848 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1849 }
1850}
1851
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001853 this->internalDrawPaint(paint);
1854}
1855
1856void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001857 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858
1859 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001860 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861 }
1862
reed@google.com4e2b3d32011-04-07 14:18:59 +00001863 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864}
1865
1866void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1867 const SkPaint& paint) {
1868 if ((long)count <= 0) {
1869 return;
1870 }
1871
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001872 SkRect r, storage;
1873 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001874 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001875 // special-case 2 points (common for drawing a single line)
1876 if (2 == count) {
1877 r.set(pts[0], pts[1]);
1878 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001879 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001880 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001881 bounds = &paint.computeFastStrokeBounds(r, &storage);
1882 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001883 return;
1884 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001885 }
reed@google.coma584aed2012-05-16 14:06:02 +00001886
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887 SkASSERT(pts != NULL);
1888
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001889 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001890
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001892 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 }
reed@google.com4b226022011-01-11 18:32:13 +00001894
reed@google.com4e2b3d32011-04-07 14:18:59 +00001895 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896}
1897
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001898void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001899 SkRect storage;
1900 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001902 bounds = &paint.computeFastBounds(r, &storage);
1903 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904 return;
1905 }
1906 }
reed@google.com4b226022011-01-11 18:32:13 +00001907
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001908 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909
1910 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001911 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912 }
1913
reed@google.com4e2b3d32011-04-07 14:18:59 +00001914 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915}
1916
reed@google.com4ed0fb72012-12-12 20:48:18 +00001917void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001918 SkRect storage;
1919 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001920 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001921 bounds = &paint.computeFastBounds(oval, &storage);
1922 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001923 return;
1924 }
1925 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001926
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001927 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001928
1929 while (iter.next()) {
1930 iter.fDevice->drawOval(iter, oval, looper.paint());
1931 }
1932
1933 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001934}
1935
1936void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001937 SkRect storage;
1938 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001939 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001940 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1941 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001942 return;
1943 }
1944 }
1945
1946 if (rrect.isRect()) {
1947 // call the non-virtual version
1948 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001949 return;
1950 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001951 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001952 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1953 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001954 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001955
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001956 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001957
1958 while (iter.next()) {
1959 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1960 }
1961
1962 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001963}
1964
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001965void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1966 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001967 SkRect storage;
1968 const SkRect* bounds = NULL;
1969 if (paint.canComputeFastBounds()) {
1970 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1971 if (this->quickReject(*bounds)) {
1972 return;
1973 }
1974 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001975
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001976 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001977
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001978 while (iter.next()) {
1979 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1980 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001981
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001982 LOOPER_END
1983}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001984
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001985void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001986 if (!path.isFinite()) {
1987 return;
1988 }
1989
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001990 SkRect storage;
1991 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001992 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001993 const SkRect& pathBounds = path.getBounds();
1994 bounds = &paint.computeFastBounds(pathBounds, &storage);
1995 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 return;
1997 }
1998 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001999
2000 const SkRect& r = path.getBounds();
2001 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002002 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002003 this->internalDrawPaint(paint);
2004 }
2005 return;
2006 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002008 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009
2010 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002011 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 }
2013
reed@google.com4e2b3d32011-04-07 14:18:59 +00002014 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002015}
2016
2017void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2018 const SkPaint* paint) {
2019 SkDEBUGCODE(bitmap.validate();)
2020
reed@google.com3d608122011-11-21 15:16:16 +00002021 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002022 SkRect bounds = {
2023 x, y,
2024 x + SkIntToScalar(bitmap.width()),
2025 y + SkIntToScalar(bitmap.height())
2026 };
2027 if (paint) {
2028 (void)paint->computeFastBounds(bounds, &bounds);
2029 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002030 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002031 return;
2032 }
2033 }
reed@google.com4b226022011-01-11 18:32:13 +00002034
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 SkMatrix matrix;
2036 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002037 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038}
2039
reed@google.com9987ec32011-09-07 11:57:52 +00002040// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002041void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002042 const SkRect& dst, const SkPaint* paint,
2043 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002044 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002045 return;
2046 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002047
reed@google.comea033602012-12-14 13:13:55 +00002048 CHECK_LOCKCOUNT_BALANCE(bitmap);
2049
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002050 SkRect storage;
2051 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002052 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002053 if (paint) {
2054 bounds = &paint->computeFastBounds(dst, &storage);
2055 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002056 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002057 return;
2058 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059 }
reed@google.com3d608122011-11-21 15:16:16 +00002060
reed@google.com33535f32012-09-25 15:37:50 +00002061 SkLazyPaint lazy;
2062 if (NULL == paint) {
2063 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002065
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002066 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002067
reed@google.com33535f32012-09-25 15:37:50 +00002068 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002069 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002070 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002071
reed@google.com33535f32012-09-25 15:37:50 +00002072 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073}
2074
reed@google.com71121732012-09-18 15:14:33 +00002075void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002076 const SkRect& dst, const SkPaint* paint,
2077 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002078 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002079 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002080}
2081
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2083 const SkPaint* paint) {
2084 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002085 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086}
2087
reed@google.com9987ec32011-09-07 11:57:52 +00002088void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2089 const SkIRect& center, const SkRect& dst,
2090 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002091 if (bitmap.drawsNothing()) {
2092 return;
2093 }
reed@google.com3d608122011-11-21 15:16:16 +00002094 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002095 SkRect storage;
2096 const SkRect* bounds = &dst;
2097 if (paint) {
2098 bounds = &paint->computeFastBounds(dst, &storage);
2099 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002100 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002101 return;
2102 }
2103 }
2104
reed@google.com9987ec32011-09-07 11:57:52 +00002105 const int32_t w = bitmap.width();
2106 const int32_t h = bitmap.height();
2107
2108 SkIRect c = center;
2109 // pin center to the bounds of the bitmap
2110 c.fLeft = SkMax32(0, center.fLeft);
2111 c.fTop = SkMax32(0, center.fTop);
2112 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2113 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2114
reed@google.com71121732012-09-18 15:14:33 +00002115 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002116 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002117 };
2118 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002119 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002120 };
reed@google.com9987ec32011-09-07 11:57:52 +00002121 SkScalar dstX[4] = {
2122 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2123 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2124 };
2125 SkScalar dstY[4] = {
2126 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2127 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2128 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002129
reed@google.com9987ec32011-09-07 11:57:52 +00002130 if (dstX[1] > dstX[2]) {
2131 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2132 dstX[2] = dstX[1];
2133 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002134
reed@google.com9987ec32011-09-07 11:57:52 +00002135 if (dstY[1] > dstY[2]) {
2136 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2137 dstY[2] = dstY[1];
2138 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002139
reed@google.com9987ec32011-09-07 11:57:52 +00002140 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002141 SkRect s, d;
2142
reed@google.com9987ec32011-09-07 11:57:52 +00002143 s.fTop = srcY[y];
2144 s.fBottom = srcY[y+1];
2145 d.fTop = dstY[y];
2146 d.fBottom = dstY[y+1];
2147 for (int x = 0; x < 3; x++) {
2148 s.fLeft = srcX[x];
2149 s.fRight = srcX[x+1];
2150 d.fLeft = dstX[x];
2151 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002152 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002153 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002154 }
2155 }
2156}
2157
2158void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2159 const SkRect& dst, const SkPaint* paint) {
2160 SkDEBUGCODE(bitmap.validate();)
2161
2162 // Need a device entry-point, so gpu can use a mesh
2163 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2164}
2165
reed@google.comf67e4cf2011-03-15 20:56:58 +00002166class SkDeviceFilteredPaint {
2167public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002168 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2169 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002170 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002171 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002172 newPaint->setFlags(flags.fFlags);
2173 newPaint->setHinting(flags.fHinting);
2174 fPaint = newPaint;
2175 } else {
2176 fPaint = &paint;
2177 }
2178 }
2179
reed@google.comf67e4cf2011-03-15 20:56:58 +00002180 const SkPaint& paint() const { return *fPaint; }
2181
2182private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002183 const SkPaint* fPaint;
2184 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002185};
2186
bungeman@google.com52c748b2011-08-22 21:30:43 +00002187void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2188 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002189 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002190 draw.fDevice->drawRect(draw, r, paint);
2191 } else {
2192 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002193 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002194 draw.fDevice->drawRect(draw, r, p);
2195 }
2196}
2197
2198void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2199 const char text[], size_t byteLength,
2200 SkScalar x, SkScalar y) {
2201 SkASSERT(byteLength == 0 || text != NULL);
2202
2203 // nothing to draw
2204 if (text == NULL || byteLength == 0 ||
2205 draw.fClip->isEmpty() ||
2206 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2207 return;
2208 }
2209
2210 SkScalar width = 0;
2211 SkPoint start;
2212
2213 start.set(0, 0); // to avoid warning
2214 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2215 SkPaint::kStrikeThruText_Flag)) {
2216 width = paint.measureText(text, byteLength);
2217
2218 SkScalar offsetX = 0;
2219 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2220 offsetX = SkScalarHalf(width);
2221 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2222 offsetX = width;
2223 }
2224 start.set(x - offsetX, y);
2225 }
2226
2227 if (0 == width) {
2228 return;
2229 }
2230
2231 uint32_t flags = paint.getFlags();
2232
2233 if (flags & (SkPaint::kUnderlineText_Flag |
2234 SkPaint::kStrikeThruText_Flag)) {
2235 SkScalar textSize = paint.getTextSize();
2236 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2237 SkRect r;
2238
2239 r.fLeft = start.fX;
2240 r.fRight = start.fX + width;
2241
2242 if (flags & SkPaint::kUnderlineText_Flag) {
2243 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2244 start.fY);
2245 r.fTop = offset;
2246 r.fBottom = offset + height;
2247 DrawRect(draw, paint, r, textSize);
2248 }
2249 if (flags & SkPaint::kStrikeThruText_Flag) {
2250 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2251 start.fY);
2252 r.fTop = offset;
2253 r.fBottom = offset + height;
2254 DrawRect(draw, paint, r, textSize);
2255 }
2256 }
2257}
2258
reed@google.come0d9ce82014-04-23 04:00:17 +00002259void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2260 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002261 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262
2263 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002264 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002265 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002266 DrawTextDecorations(iter, dfp.paint(),
2267 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268 }
2269
reed@google.com4e2b3d32011-04-07 14:18:59 +00002270 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271}
2272
reed@google.come0d9ce82014-04-23 04:00:17 +00002273void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2274 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002275 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002276
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002278 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002280 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002282
reed@google.com4e2b3d32011-04-07 14:18:59 +00002283 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284}
2285
reed@google.come0d9ce82014-04-23 04:00:17 +00002286void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2287 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002288 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002289
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002291 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002293 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002295
reed@google.com4e2b3d32011-04-07 14:18:59 +00002296 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297}
2298
reed@google.come0d9ce82014-04-23 04:00:17 +00002299void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2300 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002301 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002302
reed@android.com8a1c16f2008-12-17 15:59:43 +00002303 while (iter.next()) {
2304 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002305 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002307
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002308 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002309}
2310
reed@google.come0d9ce82014-04-23 04:00:17 +00002311// These will become non-virtual, so they always call the (virtual) onDraw... method
2312void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2313 const SkPaint& paint) {
2314 this->onDrawText(text, byteLength, x, y, paint);
2315}
2316void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2317 const SkPaint& paint) {
2318 this->onDrawPosText(text, byteLength, pos, paint);
2319}
2320void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2321 SkScalar constY, const SkPaint& paint) {
2322 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2323}
2324void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2325 const SkMatrix* matrix, const SkPaint& paint) {
2326 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2327}
2328
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2330 const SkPoint verts[], const SkPoint texs[],
2331 const SkColor colors[], SkXfermode* xmode,
2332 const uint16_t indices[], int indexCount,
2333 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002334 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002335
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 while (iter.next()) {
2337 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002338 colors, xmode, indices, indexCount,
2339 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 }
reed@google.com4b226022011-01-11 18:32:13 +00002341
reed@google.com4e2b3d32011-04-07 14:18:59 +00002342 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343}
2344
2345//////////////////////////////////////////////////////////////////////////////
2346// These methods are NOT virtual, and therefore must call back into virtual
2347// methods, rather than actually drawing themselves.
2348//////////////////////////////////////////////////////////////////////////////
2349
2350void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002351 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 SkPaint paint;
2353
2354 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002355 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002356 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 }
2358 this->drawPaint(paint);
2359}
2360
reed@android.com845fdac2009-06-23 03:01:32 +00002361void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362 SkPaint paint;
2363
2364 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002365 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002366 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367 }
2368 this->drawPaint(paint);
2369}
2370
2371void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2372 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002373
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 pt.set(x, y);
2375 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2376}
2377
2378void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2379 SkPoint pt;
2380 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002381
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382 pt.set(x, y);
2383 paint.setColor(color);
2384 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2385}
2386
2387void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2388 const SkPaint& paint) {
2389 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002390
reed@android.com8a1c16f2008-12-17 15:59:43 +00002391 pts[0].set(x0, y0);
2392 pts[1].set(x1, y1);
2393 this->drawPoints(kLines_PointMode, 2, pts, paint);
2394}
2395
2396void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2397 SkScalar right, SkScalar bottom,
2398 const SkPaint& paint) {
2399 SkRect r;
2400
2401 r.set(left, top, right, bottom);
2402 this->drawRect(r, paint);
2403}
2404
2405void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2406 const SkPaint& paint) {
2407 if (radius < 0) {
2408 radius = 0;
2409 }
2410
2411 SkRect r;
2412 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002413 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002414}
2415
2416void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2417 const SkPaint& paint) {
2418 if (rx > 0 && ry > 0) {
2419 if (paint.canComputeFastBounds()) {
2420 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002421 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002422 return;
2423 }
2424 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002425 SkRRect rrect;
2426 rrect.setRectXY(r, rx, ry);
2427 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428 } else {
2429 this->drawRect(r, paint);
2430 }
2431}
2432
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2434 SkScalar sweepAngle, bool useCenter,
2435 const SkPaint& paint) {
2436 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2437 this->drawOval(oval, paint);
2438 } else {
2439 SkPath path;
2440 if (useCenter) {
2441 path.moveTo(oval.centerX(), oval.centerY());
2442 }
2443 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2444 if (useCenter) {
2445 path.close();
2446 }
2447 this->drawPath(path, paint);
2448 }
2449}
2450
2451void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2452 const SkPath& path, SkScalar hOffset,
2453 SkScalar vOffset, const SkPaint& paint) {
2454 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002455
reed@android.com8a1c16f2008-12-17 15:59:43 +00002456 matrix.setTranslate(hOffset, vOffset);
2457 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2458}
2459
reed@android.comf76bacf2009-05-13 14:00:33 +00002460///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002461void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002462 SkBaseDevice* device = this->getDevice();
2463 if (NULL != device) {
2464 device->EXPERIMENTAL_optimize(picture);
2465 }
2466}
reed@android.comf76bacf2009-05-13 14:00:33 +00002467
robertphillips9b14f262014-06-04 05:40:44 -07002468void SkCanvas::EXPERIMENTAL_purge(const SkPicture* picture) {
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002469 SkBaseDevice* device = this->getTopDevice();
2470 if (NULL != device) {
2471 device->EXPERIMENTAL_purge(picture);
2472 }
2473}
2474
robertphillips9b14f262014-06-04 05:40:44 -07002475void SkCanvas::drawPicture(const SkPicture* picture) {
2476 if (NULL != picture) {
2477 this->onDrawPicture(picture);
2478 }
2479}
2480
2481void SkCanvas::onDrawPicture(const SkPicture* picture) {
2482 SkASSERT(NULL != picture);
2483
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002484 SkBaseDevice* device = this->getTopDevice();
2485 if (NULL != device) {
2486 // Canvas has to first give the device the opportunity to render
2487 // the picture itself.
robertphillips9b14f262014-06-04 05:40:44 -07002488 if (device->EXPERIMENTAL_drawPicture(this, picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002489 return; // the device has rendered the entire picture
2490 }
2491 }
2492
robertphillips9b14f262014-06-04 05:40:44 -07002493 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002494}
2495
reed@android.com8a1c16f2008-12-17 15:59:43 +00002496///////////////////////////////////////////////////////////////////////////////
2497///////////////////////////////////////////////////////////////////////////////
2498
2499SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002500 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002501
2502 SkASSERT(canvas);
2503
2504 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2505 fDone = !fImpl->next();
2506}
2507
2508SkCanvas::LayerIter::~LayerIter() {
2509 fImpl->~SkDrawIter();
2510}
2511
2512void SkCanvas::LayerIter::next() {
2513 fDone = !fImpl->next();
2514}
2515
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002516SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002517 return fImpl->getDevice();
2518}
2519
2520const SkMatrix& SkCanvas::LayerIter::matrix() const {
2521 return fImpl->getMatrix();
2522}
2523
2524const SkPaint& SkCanvas::LayerIter::paint() const {
2525 const SkPaint* paint = fImpl->getPaint();
2526 if (NULL == paint) {
2527 paint = &fDefaultPaint;
2528 }
2529 return *paint;
2530}
2531
2532const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2533int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2534int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002535
2536///////////////////////////////////////////////////////////////////////////////
2537
fmalitac3b589a2014-06-05 12:40:07 -07002538SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002539
2540///////////////////////////////////////////////////////////////////////////////
2541
2542static bool supported_for_raster_canvas(const SkImageInfo& info) {
2543 switch (info.alphaType()) {
2544 case kPremul_SkAlphaType:
2545 case kOpaque_SkAlphaType:
2546 break;
2547 default:
2548 return false;
2549 }
2550
2551 switch (info.colorType()) {
2552 case kAlpha_8_SkColorType:
2553 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002554 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002555 break;
2556 default:
2557 return false;
2558 }
2559
2560 return true;
2561}
2562
2563SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2564 if (!supported_for_raster_canvas(info)) {
2565 return NULL;
2566 }
2567
2568 SkBitmap bitmap;
2569 if (!bitmap.allocPixels(info)) {
2570 return NULL;
2571 }
2572
2573 // should this functionality be moved into allocPixels()?
2574 if (!bitmap.info().isOpaque()) {
2575 bitmap.eraseColor(0);
2576 }
2577 return SkNEW_ARGS(SkCanvas, (bitmap));
2578}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002579
2580SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2581 if (!supported_for_raster_canvas(info)) {
2582 return NULL;
2583 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002584
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002585 SkBitmap bitmap;
2586 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2587 return NULL;
2588 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002589 return SkNEW_ARGS(SkCanvas, (bitmap));
2590}