blob: c35f28443284035391587b802c74d12ec0f39d88 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
reed9f014712014-06-18 15:51:20 -07008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000011#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
13#include "SkDrawFilter.h"
14#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000015#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000016#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000018#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000019#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000020#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000021#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000023#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000024#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000027#if SK_SUPPORT_GPU
28#include "GrRenderTarget.h"
29#endif
30
reed@google.comda17f752012-08-16 18:27:05 +000031// experimental for faster tiled drawing...
32//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000033
reed@android.com8a1c16f2008-12-17 15:59:43 +000034//#define SK_TRACE_SAVERESTORE
35
36#ifdef SK_TRACE_SAVERESTORE
37 static int gLayerCounter;
38 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
39 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
40
41 static int gRecCounter;
42 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
43 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
44
45 static int gCanvasCounter;
46 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
47 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
48#else
49 #define inc_layer()
50 #define dec_layer()
51 #define inc_rec()
52 #define dec_rec()
53 #define inc_canvas()
54 #define dec_canvas()
55#endif
56
reed@google.comea033602012-12-14 13:13:55 +000057#ifdef SK_DEBUG
58#include "SkPixelRef.h"
59
reed@google.comf53d0a92013-01-30 13:17:32 +000060/*
61 * Some pixelref subclasses can support being "locked" from another thread
62 * during the lock-scope of skia calling them. In these instances, this balance
63 * check will fail, but may not be indicative of a problem, so we allow a build
64 * flag to disable this check.
65 *
66 * Potentially another fix would be to have a (debug-only) virtual or flag on
67 * pixelref, which could tell us at runtime if this check is valid. That would
68 * eliminate the need for this heavy-handed build check.
69 */
70#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
71class AutoCheckLockCountBalance {
72public:
73 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
74};
75#else
reed@google.comea033602012-12-14 13:13:55 +000076class AutoCheckLockCountBalance {
77public:
78 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
79 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
80 }
81 ~AutoCheckLockCountBalance() {
82 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
83 SkASSERT(count == fLockCount);
84 }
85
86private:
87 const SkPixelRef* fPixelRef;
88 int fLockCount;
89};
reed@google.comf53d0a92013-01-30 13:17:32 +000090#endif
reed@google.comea033602012-12-14 13:13:55 +000091
reed@google.comea033602012-12-14 13:13:55 +000092#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000093
94#else
95 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
reed@google.comea033602012-12-14 13:13:55 +000096#endif
97
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000098typedef SkTLazy<SkPaint> SkLazyPaint;
99
reed@google.com97af1a62012-08-28 12:19:02 +0000100void SkCanvas::predrawNotify() {
101 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000102 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000103 }
104}
105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000108/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 The clip/matrix/proc are fields that reflect the top of the save/restore
110 stack. Whenever the canvas changes, it marks a dirty flag, and then before
111 these are used (assuming we're not on a layer) we rebuild these cache
112 values: they reflect the top of the save stack, but translated and clipped
113 by the device's XY offset and bitmap-bounds.
114*/
115struct DeviceCM {
116 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000117 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000118 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000120 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000122 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 : fNext(NULL) {
124 if (NULL != device) {
125 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000126 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000130 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000132 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000134 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 fDevice->unref();
136 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000137 SkDELETE(fPaint);
138 }
reed@google.com4b226022011-01-11 18:32:13 +0000139
reed@google.com045e62d2011-10-24 12:19:46 +0000140 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
141 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000142 int x = fDevice->getOrigin().x();
143 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 int width = fDevice->width();
145 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 if ((x | y) == 0) {
148 fMatrix = &totalMatrix;
149 fClip = totalClip;
150 } else {
151 fMatrixStorage = totalMatrix;
152 fMatrixStorage.postTranslate(SkIntToScalar(-x),
153 SkIntToScalar(-y));
154 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000155
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 totalClip.translate(-x, -y, &fClip);
157 }
158
reed@google.com045e62d2011-10-24 12:19:46 +0000159 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160
161 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000162
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000164 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 SkRegion::kDifference_Op);
166 }
reed@google.com4b226022011-01-11 18:32:13 +0000167
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000168 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170#ifdef SK_DEBUG
171 if (!fClip.isEmpty()) {
172 SkIRect deviceR;
173 deviceR.set(0, 0, width, height);
174 SkASSERT(deviceR.contains(fClip.getBounds()));
175 }
176#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000177 }
178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000180 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181};
182
183/* This is the record we keep for each save/restore level in the stack.
184 Since a level optionally copies the matrix and/or stack, we have pointers
185 for these fields. If the value is copied for this level, the copy is
186 stored in the ...Storage field, and the pointer points to that. If the
187 value is not copied for this level, we ignore ...Storage, and just point
188 at the corresponding value in the previous level in the stack.
189*/
190class SkCanvas::MCRec {
191public:
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
Florin Malita5f6102d2014-06-30 10:13:28 -0400205 MCRec(const MCRec* prev) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 if (NULL != prev) {
Florin Malita5f6102d2014-06-30 10:13:28 -0400207 fMatrixStorage = *prev->fMatrix;
208 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000209
Florin Malita5f6102d2014-06-30 10:13:28 -0400210 fRasterClipStorage = *prev->fRasterClip;
211 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212
213 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000214 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215
216 fTopLayer = prev->fTopLayer;
217 } else { // no prev
218 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000219
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000221 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 fFilter = NULL;
223 fTopLayer = NULL;
224 }
225 fLayer = NULL;
226
227 // don't bother initializing fNext
228 inc_rec();
229 }
230 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000231 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 SkDELETE(fLayer);
233 dec_rec();
234 }
reed@google.com4b226022011-01-11 18:32:13 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236private:
reed@google.com00177082011-10-12 14:34:30 +0000237 SkMatrix fMatrixStorage;
238 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239};
240
241class SkDrawIter : public SkDraw {
242public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000243 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000244 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000245 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 canvas->updateDeviceCMCache();
247
reed@google.com90c07ea2012-04-13 13:50:27 +0000248 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000250 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 }
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 bool next() {
254 // skip over recs with empty clips
255 if (fSkipEmptyClips) {
256 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
257 fCurrLayer = fCurrLayer->fNext;
258 }
259 }
260
reed@google.comf68c5e22012-02-24 16:38:58 +0000261 const DeviceCM* rec = fCurrLayer;
262 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263
264 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000265 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
266 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 fDevice = rec->fDevice;
268 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000270 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271
272 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000274
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 return true;
276 }
277 return false;
278 }
reed@google.com4b226022011-01-11 18:32:13 +0000279
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000280 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000281 int getX() const { return fDevice->getOrigin().x(); }
282 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 const SkMatrix& getMatrix() const { return *fMatrix; }
284 const SkRegion& getClip() const { return *fClip; }
285 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287private:
288 SkCanvas* fCanvas;
289 const DeviceCM* fCurrLayer;
290 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 SkBool8 fSkipEmptyClips;
292
293 typedef SkDraw INHERITED;
294};
295
296/////////////////////////////////////////////////////////////////////////////
297
298class AutoDrawLooper {
299public:
reed@google.com8926b162012-03-23 15:36:36 +0000300 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000301 bool skipLayerForImageFilter = false,
302 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000303 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000305 fPaint = NULL;
306 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000307 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000308 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309
reed@google.com8926b162012-03-23 15:36:36 +0000310 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
311 SkPaint tmp;
312 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000313 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
314 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000315 // we'll clear the imageFilter for the actual draws in next(), so
316 // it will only be applied during the restore().
317 fDoClearImageFilter = true;
318 }
319
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000320 if (SkDrawLooper* looper = paint.getLooper()) {
321 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
322 looper->contextSize());
323 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000324 fIsSimple = false;
325 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000326 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000327 // can we be marked as simple?
328 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000329 }
330 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000331
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000333 if (fDoClearImageFilter) {
334 fCanvas->internalRestore();
335 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000336 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000338
reed@google.com4e2b3d32011-04-07 14:18:59 +0000339 const SkPaint& paint() const {
340 SkASSERT(fPaint);
341 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000343
reed@google.com129ec222012-05-15 13:24:09 +0000344 bool next(SkDrawFilter::Type drawType) {
345 if (fDone) {
346 return false;
347 } else if (fIsSimple) {
348 fDone = true;
349 fPaint = &fOrigPaint;
350 return !fPaint->nothingToDraw();
351 } else {
352 return this->doNext(drawType);
353 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000354 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000355
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000357 SkLazyPaint fLazyPaint;
358 SkCanvas* fCanvas;
359 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000360 SkDrawFilter* fFilter;
361 const SkPaint* fPaint;
362 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000363 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000364 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000365 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000366 SkDrawLooper::Context* fLooperContext;
367 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000368
369 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370};
371
reed@google.com129ec222012-05-15 13:24:09 +0000372bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000373 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000374 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000375 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000376
377 SkPaint* paint = fLazyPaint.set(fOrigPaint);
378
379 if (fDoClearImageFilter) {
380 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000381 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000382
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000383 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000384 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000385 return false;
386 }
387 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000388 if (!fFilter->filter(paint, drawType)) {
389 fDone = true;
390 return false;
391 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000392 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000393 // no looper means we only draw once
394 fDone = true;
395 }
396 }
397 fPaint = paint;
398
399 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000400 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000401 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000402 }
403
404 // call this after any possible paint modifiers
405 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000406 fPaint = NULL;
407 return false;
408 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000409 return true;
410}
411
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412#include "SkColorPriv.h"
413
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414////////// macros to place around the internal draw calls //////////////////
415
reed@google.com8926b162012-03-23 15:36:36 +0000416#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000417 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000418 AutoDrawLooper looper(this, paint, true); \
419 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000420 SkDrawIter iter(this);
421
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000422#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000423 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000424 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000425 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000427
reed@google.com4e2b3d32011-04-07 14:18:59 +0000428#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429
430////////////////////////////////////////////////////////////////////////////
431
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000432SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000433 fCachedLocalClipBounds.setEmpty();
434 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000435 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000436 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000437 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000438 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000439 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000440 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441
442 fMCRec = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400443 new (fMCRec) MCRec(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000445 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447
reed@google.com97af1a62012-08-28 12:19:02 +0000448 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000449
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000450 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451}
452
reed@google.comcde92112011-07-06 20:00:52 +0000453SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000454 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
455{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000456 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000457
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000458 this->init(NULL);
459}
460
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000461SkCanvas::SkCanvas(int width, int height)
462 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
463{
464 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000465
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000466 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000467 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000468 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
469}
470
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000471SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000472 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
473{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 inc_canvas();
475
476 this->init(device);
477}
478
479SkCanvas::SkCanvas(const SkBitmap& bitmap)
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
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000484 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485}
486
487SkCanvas::~SkCanvas() {
488 // free up the contents of our deque
489 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000490 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000491
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 this->internalRestore(); // restore the last, since we're going away
493
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000494 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000495
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 dec_canvas();
497}
498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499SkDrawFilter* SkCanvas::getDrawFilter() const {
500 return fMCRec->fFilter;
501}
502
503SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
504 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
505 return filter;
506}
507
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000508SkMetaData& SkCanvas::getMetaData() {
509 // metadata users are rare, so we lazily allocate it. If that changes we
510 // can decide to just make it a field in the device (rather than a ptr)
511 if (NULL == fMetaData) {
512 fMetaData = new SkMetaData;
513 }
514 return *fMetaData;
515}
516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517///////////////////////////////////////////////////////////////////////////////
518
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000519void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000520 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000521 if (device) {
522 device->flush();
523 }
524}
525
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000526SkISize SkCanvas::getTopLayerSize() const {
527 SkBaseDevice* d = this->getTopDevice();
528 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
529}
530
531SkIPoint SkCanvas::getTopLayerOrigin() const {
532 SkBaseDevice* d = this->getTopDevice();
533 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
534}
535
536SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000537 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000538 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
539}
540
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000541SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000543 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 SkASSERT(rec && rec->fLayer);
545 return rec->fLayer->fDevice;
546}
547
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000548SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000549 if (updateMatrixClip) {
550 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
551 }
reed@google.com9266fed2011-03-30 00:18:03 +0000552 return fMCRec->fTopLayer->fDevice;
553}
554
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000555SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000557 SkDeque::F2BIter iter(fMCStack);
558 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000560 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561
562 if (rootDevice == device) {
563 return device;
564 }
reed@google.com4b226022011-01-11 18:32:13 +0000565
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000567 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 }
569 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000570 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 }
572
573 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
574 rootDevice = device;
575
576 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000577
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 /* Now we update our initial region to have the bounds of the new device,
579 and then intersect all of the clips in our stack with these bounds,
580 to ensure that we can't draw outside of the device's bounds (and trash
581 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000582
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 NOTE: this is only a partial-fix, since if the new device is larger than
584 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000585 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
587 reconstruct the correct clips, so this approximation will have to do.
588 The caller really needs to restore() back to the base if they want to
589 accurately take advantage of the new device bounds.
590 */
591
reed@google.com42aea282012-03-28 16:19:15 +0000592 SkIRect bounds;
593 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000595 } else {
596 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 }
reed@google.com42aea282012-03-28 16:19:15 +0000598 // now jam our 1st clip to be bounds, and intersect the rest with that
599 rec->fRasterClip->setRect(bounds);
600 while ((rec = (MCRec*)iter.next()) != NULL) {
601 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
602 }
603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 return device;
605}
606
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000607bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
608 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
609 return false;
610 }
611
612 bool weAllocated = false;
613 if (NULL == bitmap->pixelRef()) {
614 if (!bitmap->allocPixels()) {
615 return false;
616 }
617 weAllocated = true;
618 }
619
620 SkBitmap bm(*bitmap);
621 bm.lockPixels();
622 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
623 return true;
624 }
625
626 if (weAllocated) {
627 bitmap->setPixelRef(NULL);
628 }
629 return false;
630}
reed@google.com51df9e32010-12-23 19:29:18 +0000631
bsalomon@google.comc6980972011-11-02 19:57:21 +0000632bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000633 SkIRect r = srcRect;
634 const SkISize size = this->getBaseLayerSize();
635 if (!r.intersect(0, 0, size.width(), size.height())) {
636 bitmap->reset();
637 return false;
638 }
639
640 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
641 // bitmap will already be reset.
642 return false;
643 }
644 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
645 bitmap->reset();
646 return false;
647 }
648 return true;
649}
650
651bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
652 switch (origInfo.colorType()) {
653 case kUnknown_SkColorType:
654 case kIndex_8_SkColorType:
655 return false;
656 default:
657 break;
658 }
659 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
660 return false;
661 }
662 if (0 == origInfo.width() || 0 == origInfo.height()) {
663 return false;
664 }
665
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000666 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000667 if (!device) {
668 return false;
669 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000670
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000671 const SkISize size = this->getBaseLayerSize();
672 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
673 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000674 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000675 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000676
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000677 SkImageInfo info = origInfo;
678 // the intersect may have shrunk info's logical size
679 info.fWidth = srcR.width();
680 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000681
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000682 // if x or y are negative, then we have to adjust pixels
683 if (x > 0) {
684 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000685 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000686 if (y > 0) {
687 y = 0;
688 }
689 // here x,y are either 0 or negative
690 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000691
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000692 // The device can assert that the requested area is always contained in its bounds
693 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000694}
695
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000696bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
697 if (bitmap.getTexture()) {
698 return false;
699 }
700 SkBitmap bm(bitmap);
701 bm.lockPixels();
702 if (bm.getPixels()) {
703 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
704 }
705 return false;
706}
707
708bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
709 int x, int y) {
710 switch (origInfo.colorType()) {
711 case kUnknown_SkColorType:
712 case kIndex_8_SkColorType:
713 return false;
714 default:
715 break;
716 }
717 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
718 return false;
719 }
720
721 const SkISize size = this->getBaseLayerSize();
722 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
723 if (!target.intersect(0, 0, size.width(), size.height())) {
724 return false;
725 }
726
727 SkBaseDevice* device = this->getDevice();
728 if (!device) {
729 return false;
730 }
731
732 SkImageInfo info = origInfo;
733 // the intersect may have shrunk info's logical size
734 info.fWidth = target.width();
735 info.fHeight = target.height();
736
737 // if x or y are negative, then we have to adjust pixels
738 if (x > 0) {
739 x = 0;
740 }
741 if (y > 0) {
742 y = 0;
743 }
744 // here x,y are either 0 or negative
745 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
746
reed4af35f32014-06-27 17:47:49 -0700747 // Tell our owning surface to bump its generation ID
748 this->predrawNotify();
749
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000750 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000751 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000752}
reed@google.com51df9e32010-12-23 19:29:18 +0000753
junov@google.com4370aed2012-01-18 16:21:08 +0000754SkCanvas* SkCanvas::canvasForDrawIter() {
755 return this;
756}
757
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758//////////////////////////////////////////////////////////////////////////////
759
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760void SkCanvas::updateDeviceCMCache() {
761 if (fDeviceCMDirty) {
762 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000763 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000767 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000769 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000771 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 } while ((layer = layer->fNext) != NULL);
773 }
774 fDeviceCMDirty = false;
775 }
776}
777
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778///////////////////////////////////////////////////////////////////////////////
779
Florin Malita5f6102d2014-06-30 10:13:28 -0400780int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400784 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000786
Florin Malita5f6102d2014-06-30 10:13:28 -0400787 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 return saveCount;
790}
791
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000792int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400793 this->willSave();
794 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795}
796
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000798#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000800#else
801 return true;
802#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803}
804
junov@chromium.orga907ac32012-02-24 21:54:07 +0000805bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000806 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000807 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000808 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000809 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000811 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000812
813 if (imageFilter) {
814 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
815 // Filters may grow the bounds beyond the device bounds.
816 op = SkRegion::kReplace_Op;
817 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000818 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 if (NULL != bounds) {
820 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000821
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 this->getTotalMatrix().mapRect(&r, *bounds);
823 r.roundOut(&ir);
824 // early exit if the layer's bounds are clipped out
825 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000826 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000827 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000828 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000829 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 }
831 } else { // no user bounds, so just use the clip
832 ir = clipBounds;
833 }
834
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000835 if (bounds_affects_clip(flags)) {
836 fClipStack.clipDevRect(ir, op);
837 // early exit if the clip is now empty
838 if (!fMCRec->fRasterClip->op(ir, op)) {
839 return false;
840 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000841 }
842
843 if (intersection) {
844 *intersection = ir;
845 }
846 return true;
847}
848
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000849int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
850 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
851 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
852}
853
junov@chromium.orga907ac32012-02-24 21:54:07 +0000854int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
855 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000856 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
857 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000858}
859
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000860static SkBaseDevice* create_compatible_device(SkCanvas* canvas,
861 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000862 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000863 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000864}
865
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000866int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
867 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000868#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000869 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000870#endif
871
junov@chromium.orga907ac32012-02-24 21:54:07 +0000872 // do this before we create the layer. We don't call the public save() since
873 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400874 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000875
876 fDeviceCMDirty = true;
877
878 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000879 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 return count;
881 }
882
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000883 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
884 // the clipRectBounds() call above?
885 if (kNoLayer_SaveLayerStrategy == strategy) {
886 return count;
887 }
888
reed@google.comb55deeb2012-01-06 14:43:09 +0000889 // Kill the imagefilter if our device doesn't allow it
890 SkLazyPaint lazyP;
891 if (paint && paint->getImageFilter()) {
892 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000893 if (justForImageFilter) {
894 // early exit if the layer was just for the imageFilter
895 return count;
896 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000897 SkPaint* p = lazyP.set(*paint);
898 p->setImageFilter(NULL);
899 paint = p;
900 }
901 }
902
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000903 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
904 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
905 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000907 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000908 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org2b290ce2014-03-25 23:29:53 +0000909 device = create_compatible_device(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000910 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000911 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000912 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000913 if (NULL == device) {
914 SkDebugf("Unable to create device for layer.");
915 return count;
916 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000917
reed@google.com6f8f2922011-03-04 22:27:10 +0000918 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000919 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 device->unref();
921
922 layer->fNext = fMCRec->fTopLayer;
923 fMCRec->fLayer = layer;
924 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
925
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000926 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 return count;
928}
929
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000930int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
931 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
932}
933
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
935 SaveFlags flags) {
936 if (0xFF == alpha) {
937 return this->saveLayer(bounds, NULL, flags);
938 } else {
939 SkPaint tmpPaint;
940 tmpPaint.setAlpha(alpha);
941 return this->saveLayer(bounds, &tmpPaint, flags);
942 }
943}
944
945void SkCanvas::restore() {
946 // check for underflow
947 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000948 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 this->internalRestore();
950 }
951}
952
953void SkCanvas::internalRestore() {
954 SkASSERT(fMCStack.count() != 0);
955
956 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000957 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958
Florin Malita5f6102d2014-06-30 10:13:28 -0400959 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000960
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000961 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 DeviceCM* layer = fMCRec->fLayer; // may be null
963 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
964 fMCRec->fLayer = NULL;
965
966 // now do the normal restore()
967 fMCRec->~MCRec(); // balanced in save()
968 fMCStack.pop_back();
969 fMCRec = (MCRec*)fMCStack.back();
970
971 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
972 since if we're being recorded, we don't want to record this (the
973 recorder will have already recorded the restore).
974 */
975 if (NULL != layer) {
976 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000977 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000978 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
979 layer->fPaint);
980 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000982
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000983 SkASSERT(fSaveLayerCount > 0);
984 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 }
986 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000987 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988}
989
990int SkCanvas::getSaveCount() const {
991 return fMCStack.count();
992}
993
994void SkCanvas::restoreToCount(int count) {
995 // sanity check
996 if (count < 1) {
997 count = 1;
998 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000999
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001000 int n = this->getSaveCount() - count;
1001 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 this->restore();
1003 }
1004}
1005
reed@google.com7c202932011-12-14 18:48:05 +00001006bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001007 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001008}
1009
reed@google.com76f10a32014-02-05 15:32:21 +00001010SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1011 return this->onNewSurface(info);
1012}
1013
1014SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1015 SkBaseDevice* dev = this->getDevice();
1016 return dev ? dev->newSurface(info) : NULL;
1017}
1018
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001019SkImageInfo SkCanvas::imageInfo() const {
1020 SkBaseDevice* dev = this->getDevice();
1021 if (dev) {
1022 return dev->imageInfo();
1023 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001024 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001025 }
1026}
1027
1028const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1029 return this->onPeekPixels(info, rowBytes);
1030}
1031
1032const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1033 SkBaseDevice* dev = this->getDevice();
1034 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1035}
1036
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001037void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1038 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1039 if (pixels && origin) {
1040 *origin = this->getTopDevice(false)->getOrigin();
1041 }
1042 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001043}
1044
1045void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1046 SkBaseDevice* dev = this->getTopDevice();
1047 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1048}
1049
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001050SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1051 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1052 if (NULL == fAddr) {
1053 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001054 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001055 return; // failure, fAddr is NULL
1056 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001057 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1058 return; // failure, fAddr is NULL
1059 }
1060 fAddr = fBitmap.getPixels();
1061 fRowBytes = fBitmap.rowBytes();
1062 }
1063 SkASSERT(fAddr); // success
1064}
1065
1066bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1067 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001068 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001069 } else {
1070 bitmap->reset();
1071 return false;
1072 }
1073}
1074
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001075void SkCanvas::onPushCull(const SkRect& cullRect) {
1076 // do nothing. Subclasses may do something
1077}
1078
1079void SkCanvas::onPopCull() {
1080 // do nothing. Subclasses may do something
1081}
1082
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001084#ifdef SK_DEBUG
1085// Ensure that cull rects are monotonically nested in device space.
1086void SkCanvas::validateCull(const SkIRect& devCull) {
1087 if (fCullStack.isEmpty()
1088 || devCull.isEmpty()
1089 || fCullStack.top().contains(devCull)) {
1090 return;
1091 }
1092
1093 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1094 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1095 fCullStack.top().x(), fCullStack.top().y(),
1096 fCullStack.top().right(), fCullStack.top().bottom()));
1097
1098#ifdef ASSERT_NESTED_CULLING
1099 SkDEBUGFAIL("Invalid cull.");
1100#endif
1101}
1102#endif
1103
1104void SkCanvas::pushCull(const SkRect& cullRect) {
1105 ++fCullCount;
1106 this->onPushCull(cullRect);
1107
1108#ifdef SK_DEBUG
1109 // Map the cull rect into device space.
1110 SkRect mappedCull;
1111 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1112
1113 // Take clipping into account.
1114 SkIRect devClip, devCull;
1115 mappedCull.roundOut(&devCull);
1116 this->getClipDeviceBounds(&devClip);
1117 if (!devCull.intersect(devClip)) {
1118 devCull.setEmpty();
1119 }
1120
1121 this->validateCull(devCull);
1122 fCullStack.push(devCull); // balanced in popCull
1123#endif
1124}
1125
1126void SkCanvas::popCull() {
1127 SkASSERT(fCullStack.count() == fCullCount);
1128
1129 if (fCullCount > 0) {
1130 --fCullCount;
1131 this->onPopCull();
1132
1133 SkDEBUGCODE(fCullStack.pop());
1134 }
1135}
1136
1137/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001139void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001141 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 return;
1143 }
1144
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001145 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001147 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001149
1150 SkDEBUGCODE(bitmap.validate();)
1151 CHECK_LOCKCOUNT_BALANCE(bitmap);
1152
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001153 SkRect storage;
1154 const SkRect* bounds = NULL;
1155 if (paint && paint->canComputeFastBounds()) {
1156 bitmap.getBounds(&storage);
1157 matrix.mapRect(&storage);
1158 bounds = &paint->computeFastBounds(storage, &storage);
1159 }
1160
1161 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001162
1163 while (iter.next()) {
1164 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1165 }
1166
1167 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168}
1169
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001170void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001171 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172 SkPaint tmp;
1173 if (NULL == paint) {
1174 tmp.setDither(true);
1175 paint = &tmp;
1176 }
reed@google.com4b226022011-01-11 18:32:13 +00001177
reed@google.com8926b162012-03-23 15:36:36 +00001178 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001180 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001181 paint = &looper.paint();
1182 SkImageFilter* filter = paint->getImageFilter();
1183 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001184 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001185 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001186 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001187 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001188 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001189 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001190 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001191 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001192 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1193 SkAutoUnref aur(NULL);
1194 if (!cache) {
1195 cache = SkImageFilter::Cache::Create();
1196 aur.reset(cache);
1197 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001198 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001199 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001200 SkPaint tmpUnfiltered(*paint);
1201 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001202 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1203 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001204 }
1205 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001206 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001209 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210}
1211
reed@google.com8926b162012-03-23 15:36:36 +00001212void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1213 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001214 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001215 return;
1216 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001217 SkDEBUGCODE(bitmap.validate();)
1218 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001219
reed@google.com8926b162012-03-23 15:36:36 +00001220 SkPaint tmp;
1221 if (NULL == paint) {
1222 paint = &tmp;
1223 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001224
reed@google.com8926b162012-03-23 15:36:36 +00001225 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001226
reed@google.com8926b162012-03-23 15:36:36 +00001227 while (iter.next()) {
1228 paint = &looper.paint();
1229 SkImageFilter* filter = paint->getImageFilter();
1230 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1231 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001232 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001233 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001234 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001235 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001236 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001237 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco@chromium.org1a479e72014-04-14 15:51:48 +00001238 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache();
1239 SkAutoUnref aur(NULL);
1240 if (!cache) {
1241 cache = SkImageFilter::Cache::Create();
1242 aur.reset(cache);
1243 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001244 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001245 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001246 SkPaint tmpUnfiltered(*paint);
1247 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001248 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001249 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001250 }
1251 } else {
1252 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1253 }
1254 }
1255 LOOPER_END
1256}
1257
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001259void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001260 SkMatrix m;
1261 m.setTranslate(dx, dy);
1262 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263}
1264
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001265void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001266 SkMatrix m;
1267 m.setScale(sx, sy);
1268 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269}
1270
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001271void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001272 SkMatrix m;
1273 m.setRotate(degrees);
1274 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275}
1276
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001277void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001278 SkMatrix m;
1279 m.setSkew(sx, sy);
1280 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001281}
1282
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001283void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001284 if (matrix.isIdentity()) {
1285 return;
1286 }
1287
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001289 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001290 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001291
1292 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001293}
1294
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295void SkCanvas::setMatrix(const SkMatrix& matrix) {
1296 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001297 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001299 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300}
1301
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302void SkCanvas::resetMatrix() {
1303 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001304
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 matrix.reset();
1306 this->setMatrix(matrix);
1307}
1308
1309//////////////////////////////////////////////////////////////////////////////
1310
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001311void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001312 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1313 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001314}
1315
1316void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001317#ifdef SK_ENABLE_CLIP_QUICKREJECT
1318 if (SkRegion::kIntersect_Op == op) {
1319 if (fMCRec->fRasterClip->isEmpty()) {
1320 return false;
1321 }
1322
reed@google.com3b3e8952012-08-16 20:53:31 +00001323 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001324 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001325 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001326
1327 fClipStack.clipEmpty();
1328 return fMCRec->fRasterClip->setEmpty();
1329 }
1330 }
1331#endif
1332
reed@google.com5c3d1472011-02-22 19:12:23 +00001333 AutoValidateClip avc(this);
1334
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001336 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001337 if (!fAllowSoftClip) {
1338 edgeStyle = kHard_ClipEdgeStyle;
1339 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340
1341 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001342 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001343 // the matrix. This means we don't have to a) make a path, and b) tell
1344 // the region code to scan-convert the path, only to discover that it
1345 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347
1348 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001349 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1350 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001352 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001353 // and clip against that, since it can handle any matrix. However, to
1354 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1355 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 SkPath path;
1357
1358 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001359 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 }
1361}
1362
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001363static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1364 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001365 // base is used to limit the size (and therefore memory allocation) of the
1366 // region that results from scan converting devPath.
1367 SkRegion base;
1368
reed@google.com819c9212011-02-23 18:56:55 +00001369 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001370 // since we are intersect, we can do better (tighter) with currRgn's
1371 // bounds, than just using the device. However, if currRgn is complex,
1372 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001373 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001374 // FIXME: we should also be able to do this when currClip->isBW(),
1375 // but relaxing the test above triggers GM asserts in
1376 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001377 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001378 } else {
reed@google.com00177082011-10-12 14:34:30 +00001379 base.setRect(currClip->getBounds());
1380 SkRasterClip clip;
1381 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001382 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001383 }
reed@google.com819c9212011-02-23 18:56:55 +00001384 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001385 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001386 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001387 currClip->setEmpty();
1388 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001389 }
1390
junov@chromium.orga907ac32012-02-24 21:54:07 +00001391 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001392
1393 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001394 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001395 } else {
reed@google.com00177082011-10-12 14:34:30 +00001396 SkRasterClip clip;
1397 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001399 }
1400 }
1401}
1402
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001403void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001404 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001405 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001406 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1407 } else {
1408 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001409 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001410}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001411
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001412void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001413 SkRRect transformedRRect;
1414 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1415 AutoValidateClip avc(this);
1416
1417 fDeviceCMDirty = true;
1418 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001419 if (!fAllowSoftClip) {
1420 edgeStyle = kHard_ClipEdgeStyle;
1421 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001422
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001423 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001424
1425 SkPath devPath;
1426 devPath.addRRect(transformedRRect);
1427
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1429 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001430 }
1431
1432 SkPath path;
1433 path.addRRect(rrect);
1434 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001435 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001436}
1437
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001438void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001439 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1440 SkRect r;
1441 if (!path.isInverseFillType() && path.isRect(&r)) {
1442 this->onClipRect(r, op, edgeStyle);
1443 } else {
1444 this->onClipPath(path, op, edgeStyle);
1445 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001446}
1447
1448void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001449#ifdef SK_ENABLE_CLIP_QUICKREJECT
1450 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1451 if (fMCRec->fRasterClip->isEmpty()) {
1452 return false;
1453 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001454
reed@google.com3b3e8952012-08-16 20:53:31 +00001455 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001456 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001457 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001458
reed@google.comda17f752012-08-16 18:27:05 +00001459 fClipStack.clipEmpty();
1460 return fMCRec->fRasterClip->setEmpty();
1461 }
1462 }
1463#endif
1464
reed@google.com5c3d1472011-02-22 19:12:23 +00001465 AutoValidateClip avc(this);
1466
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001468 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001469 if (!fAllowSoftClip) {
1470 edgeStyle = kHard_ClipEdgeStyle;
1471 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472
1473 SkPath devPath;
1474 path.transform(*fMCRec->fMatrix, &devPath);
1475
reed@google.comfe701122011-11-08 19:41:23 +00001476 // Check if the transfomation, or the original path itself
1477 // made us empty. Note this can also happen if we contained NaN
1478 // values. computing the bounds detects this, and will set our
1479 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1480 if (devPath.getBounds().isEmpty()) {
1481 // resetting the path will remove any NaN or other wanky values
1482 // that might upset our scan converter.
1483 devPath.reset();
1484 }
1485
reed@google.com5c3d1472011-02-22 19:12:23 +00001486 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001488
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001489 if (fAllowSimplifyClip) {
1490 devPath.reset();
1491 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1492 const SkClipStack* clipStack = getClipStack();
1493 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1494 const SkClipStack::Element* element;
1495 while ((element = iter.next())) {
1496 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001497 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001498 if (type != SkClipStack::Element::kEmpty_Type) {
1499 element->asPath(&operand);
1500 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001501 SkRegion::Op elementOp = element->getOp();
1502 if (elementOp == SkRegion::kReplace_Op) {
1503 devPath = operand;
1504 } else {
1505 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1506 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001507 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1508 // perhaps we need an API change to avoid this sort of mixed-signals about
1509 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001510 if (element->isAA()) {
1511 edgeStyle = kSoft_ClipEdgeStyle;
1512 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001513 }
1514 op = SkRegion::kReplace_Op;
1515 }
1516
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001517 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518}
1519
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001520void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001521 bool inverseFilled) {
1522 // This is for updating the clip conservatively using only bounds
1523 // information.
1524 // Contract:
1525 // The current clip must contain the true clip. The true
1526 // clip is the clip that would have normally been computed
1527 // by calls to clipPath and clipRRect
1528 // Objective:
1529 // Keep the current clip as small as possible without
1530 // breaking the contract, using only clip bounding rectangles
1531 // (for performance).
1532
1533 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1534 // don't have to worry about getting caught in a loop. Thus anywhere
1535 // we call a virtual method, we explicitly prefix it with
1536 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001537
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001538 if (inverseFilled) {
1539 switch (op) {
1540 case SkRegion::kIntersect_Op:
1541 case SkRegion::kDifference_Op:
1542 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001543 // the clip unchanged conservatively respects the contract.
1544 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001545 case SkRegion::kUnion_Op:
1546 case SkRegion::kReplace_Op:
1547 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001548 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001549 // These ops can grow the current clip up to the extents of
1550 // the input clip, which is inverse filled, so we just set
1551 // the current clip to the device bounds.
1552 SkRect deviceBounds;
1553 SkIRect deviceIBounds;
1554 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001555 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001556
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001557 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001558 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001559 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001560 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001562 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 break;
1564 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001565 default:
1566 SkASSERT(0); // unhandled op?
1567 }
1568 } else {
1569 // Not inverse filled
1570 switch (op) {
1571 case SkRegion::kIntersect_Op:
1572 case SkRegion::kUnion_Op:
1573 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001574 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1575 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001576 case SkRegion::kDifference_Op:
1577 // Difference can only shrink the current clip.
1578 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001580 case SkRegion::kReverseDifference_Op:
1581 // To reverse, we swap in the bounds with a replace op.
1582 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1584 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001585 case SkRegion::kXOR_Op:
1586 // Be conservative, based on (A XOR B) always included in (A union B),
1587 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001588 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1589 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001590 default:
1591 SkASSERT(0); // unhandled op?
1592 }
1593 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001594}
1595
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001596void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001597 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598}
1599
1600void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001601 AutoValidateClip avc(this);
1602
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001604 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605
reed@google.com5c3d1472011-02-22 19:12:23 +00001606 // todo: signal fClipStack that we have a region, and therefore (I guess)
1607 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001608 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001609
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001610 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611}
1612
reed@google.com819c9212011-02-23 18:56:55 +00001613#ifdef SK_DEBUG
1614void SkCanvas::validateClip() const {
1615 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001616 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001617 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001618 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001619 return;
1620 }
1621
reed@google.com819c9212011-02-23 18:56:55 +00001622 SkIRect ir;
1623 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001624 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001625
robertphillips@google.com80214e22012-07-20 15:33:18 +00001626 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001627 const SkClipStack::Element* element;
1628 while ((element = iter.next()) != NULL) {
1629 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001630 case SkClipStack::Element::kRect_Type:
1631 element->getRect().round(&ir);
1632 tmpClip.op(ir, element->getOp());
1633 break;
1634 case SkClipStack::Element::kEmpty_Type:
1635 tmpClip.setEmpty();
1636 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001637 default: {
1638 SkPath path;
1639 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001641 break;
1642 }
reed@google.com819c9212011-02-23 18:56:55 +00001643 }
1644 }
reed@google.com819c9212011-02-23 18:56:55 +00001645}
1646#endif
1647
reed@google.com90c07ea2012-04-13 13:50:27 +00001648void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001649 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001650 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001651
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001652 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001653 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001654 }
1655}
1656
reed@google.com5c3d1472011-02-22 19:12:23 +00001657///////////////////////////////////////////////////////////////////////////////
1658
reed@google.com754de5f2014-02-24 19:38:20 +00001659bool SkCanvas::isClipEmpty() const {
1660 return fMCRec->fRasterClip->isEmpty();
1661}
1662
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001663bool SkCanvas::isClipRect() const {
1664 return fMCRec->fRasterClip->isRect();
1665}
1666
reed@google.com3b3e8952012-08-16 20:53:31 +00001667bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001668
reed@google.com16078632011-12-06 18:56:37 +00001669 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001670 return true;
1671
reed@google.com00177082011-10-12 14:34:30 +00001672 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 return true;
1674 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675
tomhudson@google.com8d430182011-06-06 19:11:19 +00001676 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001677 SkRect dst;
1678 fMCRec->fMatrix->mapRect(&dst, rect);
1679 SkIRect idst;
1680 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001681 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001682 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001683 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001684
reed@android.coma380ae42009-07-21 01:17:02 +00001685 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001686 // TODO: should we use | instead, or compare all 4 at once?
1687 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001688 return true;
1689 }
reed@google.comc0784db2013-12-13 21:16:12 +00001690 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001691 return true;
1692 }
1693 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695}
1696
reed@google.com3b3e8952012-08-16 20:53:31 +00001697bool SkCanvas::quickReject(const SkPath& path) const {
1698 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699}
1700
reed@google.com3b3e8952012-08-16 20:53:31 +00001701bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001702 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001703 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 return false;
1705 }
1706
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001707 SkMatrix inverse;
1708 // if we can't invert the CTM, we can't return local clip bounds
1709 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001710 if (bounds) {
1711 bounds->setEmpty();
1712 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001713 return false;
1714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001716 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001717 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001718 // adjust it outwards in case we are antialiasing
1719 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001720
reed@google.com8f4d2302013-12-17 16:44:46 +00001721 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1722 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 inverse.mapRect(bounds, r);
1724 }
1725 return true;
1726}
1727
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001728bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001729 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001730 if (clip.isEmpty()) {
1731 if (bounds) {
1732 bounds->setEmpty();
1733 }
1734 return false;
1735 }
1736
1737 if (NULL != bounds) {
1738 *bounds = clip.getBounds();
1739 }
1740 return true;
1741}
1742
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743const SkMatrix& SkCanvas::getTotalMatrix() const {
1744 return *fMCRec->fMatrix;
1745}
1746
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001747#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001748SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001749 if (fMCRec->fRasterClip->isEmpty()) {
1750 return kEmpty_ClipType;
1751 }
1752 if (fMCRec->fRasterClip->isRect()) {
1753 return kRect_ClipType;
1754 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001755 return kComplex_ClipType;
1756}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001757#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001758
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001759const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1760 return fMCRec->fRasterClip->forceGetBW();
1761}
1762
1763void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1764 path->reset();
1765
1766 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1767 if (rgn.isEmpty()) {
1768 return;
1769 }
1770 (void)rgn.getBoundaryPath(path);
1771}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772
reed@google.com9c135db2014-03-12 18:28:35 +00001773GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1774 SkBaseDevice* dev = this->getTopDevice();
1775 return dev ? dev->accessRenderTarget() : NULL;
1776}
1777
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001778SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001779 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001780 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781}
1782
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001783GrContext* SkCanvas::getGrContext() {
1784#if SK_SUPPORT_GPU
1785 SkBaseDevice* device = this->getTopDevice();
1786 if (NULL != device) {
1787 GrRenderTarget* renderTarget = device->accessRenderTarget();
1788 if (NULL != renderTarget) {
1789 return renderTarget->getContext();
1790 }
1791 }
1792#endif
1793
1794 return NULL;
1795
1796}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001797
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001798void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1799 const SkPaint& paint) {
1800 if (outer.isEmpty()) {
1801 return;
1802 }
1803 if (inner.isEmpty()) {
1804 this->drawRRect(outer, paint);
1805 return;
1806 }
1807
1808 // We don't have this method (yet), but technically this is what we should
1809 // be able to assert...
1810 // SkASSERT(outer.contains(inner));
1811 //
1812 // For now at least check for containment of bounds
1813 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1814
1815 this->onDrawDRRect(outer, inner, paint);
1816}
1817
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818//////////////////////////////////////////////////////////////////////////////
1819// These are the virtual drawing methods
1820//////////////////////////////////////////////////////////////////////////////
1821
reed@google.com2a981812011-04-14 18:59:28 +00001822void SkCanvas::clear(SkColor color) {
1823 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001824 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001825 while (iter.next()) {
1826 iter.fDevice->clear(color);
1827 }
1828}
1829
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001830void SkCanvas::onDiscard() {
1831 if (NULL != fSurfaceBase) {
1832 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1833 }
1834}
1835
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001837 this->internalDrawPaint(paint);
1838}
1839
1840void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001841 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842
1843 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001844 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 }
1846
reed@google.com4e2b3d32011-04-07 14:18:59 +00001847 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848}
1849
1850void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1851 const SkPaint& paint) {
1852 if ((long)count <= 0) {
1853 return;
1854 }
1855
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001856 SkRect r, storage;
1857 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001858 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001859 // special-case 2 points (common for drawing a single line)
1860 if (2 == count) {
1861 r.set(pts[0], pts[1]);
1862 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001863 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001864 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001865 bounds = &paint.computeFastStrokeBounds(r, &storage);
1866 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001867 return;
1868 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001869 }
reed@google.coma584aed2012-05-16 14:06:02 +00001870
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871 SkASSERT(pts != NULL);
1872
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001873 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001874
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001876 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001877 }
reed@google.com4b226022011-01-11 18:32:13 +00001878
reed@google.com4e2b3d32011-04-07 14:18:59 +00001879 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880}
1881
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001882void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001883 SkRect storage;
1884 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001886 bounds = &paint.computeFastBounds(r, &storage);
1887 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888 return;
1889 }
1890 }
reed@google.com4b226022011-01-11 18:32:13 +00001891
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001892 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893
1894 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001895 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896 }
1897
reed@google.com4e2b3d32011-04-07 14:18:59 +00001898 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899}
1900
reed@google.com4ed0fb72012-12-12 20:48:18 +00001901void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001902 SkRect storage;
1903 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001904 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001905 bounds = &paint.computeFastBounds(oval, &storage);
1906 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001907 return;
1908 }
1909 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001910
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001911 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001912
1913 while (iter.next()) {
1914 iter.fDevice->drawOval(iter, oval, looper.paint());
1915 }
1916
1917 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001918}
1919
1920void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001921 SkRect storage;
1922 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001923 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001924 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1925 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001926 return;
1927 }
1928 }
1929
1930 if (rrect.isRect()) {
1931 // call the non-virtual version
1932 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001933 return;
1934 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001935 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001936 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1937 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001938 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001939
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001940 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001941
1942 while (iter.next()) {
1943 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1944 }
1945
1946 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001947}
1948
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001949void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1950 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001951 SkRect storage;
1952 const SkRect* bounds = NULL;
1953 if (paint.canComputeFastBounds()) {
1954 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1955 if (this->quickReject(*bounds)) {
1956 return;
1957 }
1958 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001959
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001960 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001961
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001962 while (iter.next()) {
1963 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1964 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001965
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001966 LOOPER_END
1967}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001968
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001969void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001970 if (!path.isFinite()) {
1971 return;
1972 }
1973
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001974 SkRect storage;
1975 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001976 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001977 const SkRect& pathBounds = path.getBounds();
1978 bounds = &paint.computeFastBounds(pathBounds, &storage);
1979 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980 return;
1981 }
1982 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001983
1984 const SkRect& r = path.getBounds();
1985 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001986 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001987 this->internalDrawPaint(paint);
1988 }
1989 return;
1990 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001992 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001993
1994 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001995 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 }
1997
reed@google.com4e2b3d32011-04-07 14:18:59 +00001998 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999}
2000
2001void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2002 const SkPaint* paint) {
2003 SkDEBUGCODE(bitmap.validate();)
2004
reed@google.com3d608122011-11-21 15:16:16 +00002005 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002006 SkRect bounds = {
2007 x, y,
2008 x + SkIntToScalar(bitmap.width()),
2009 y + SkIntToScalar(bitmap.height())
2010 };
2011 if (paint) {
2012 (void)paint->computeFastBounds(bounds, &bounds);
2013 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002014 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002015 return;
2016 }
2017 }
reed@google.com4b226022011-01-11 18:32:13 +00002018
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019 SkMatrix matrix;
2020 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002021 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022}
2023
reed@google.com9987ec32011-09-07 11:57:52 +00002024// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002025void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002026 const SkRect& dst, const SkPaint* paint,
2027 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002028 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002029 return;
2030 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002031
reed@google.comea033602012-12-14 13:13:55 +00002032 CHECK_LOCKCOUNT_BALANCE(bitmap);
2033
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002034 SkRect storage;
2035 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002036 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002037 if (paint) {
2038 bounds = &paint->computeFastBounds(dst, &storage);
2039 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002040 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002041 return;
2042 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043 }
reed@google.com3d608122011-11-21 15:16:16 +00002044
reed@google.com33535f32012-09-25 15:37:50 +00002045 SkLazyPaint lazy;
2046 if (NULL == paint) {
2047 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002048 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002049
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002050 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002051
reed@google.com33535f32012-09-25 15:37:50 +00002052 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002053 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002054 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002055
reed@google.com33535f32012-09-25 15:37:50 +00002056 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002057}
2058
reed@google.com71121732012-09-18 15:14:33 +00002059void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002060 const SkRect& dst, const SkPaint* paint,
2061 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002062 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002063 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002064}
2065
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2067 const SkPaint* paint) {
2068 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002069 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070}
2071
reed@google.com9987ec32011-09-07 11:57:52 +00002072void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2073 const SkIRect& center, const SkRect& dst,
2074 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002075 if (bitmap.drawsNothing()) {
2076 return;
2077 }
reed@google.com3d608122011-11-21 15:16:16 +00002078 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002079 SkRect storage;
2080 const SkRect* bounds = &dst;
2081 if (paint) {
2082 bounds = &paint->computeFastBounds(dst, &storage);
2083 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002084 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002085 return;
2086 }
2087 }
2088
reed@google.com9987ec32011-09-07 11:57:52 +00002089 const int32_t w = bitmap.width();
2090 const int32_t h = bitmap.height();
2091
2092 SkIRect c = center;
2093 // pin center to the bounds of the bitmap
2094 c.fLeft = SkMax32(0, center.fLeft);
2095 c.fTop = SkMax32(0, center.fTop);
2096 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2097 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2098
reed@google.com71121732012-09-18 15:14:33 +00002099 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002100 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002101 };
2102 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002103 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002104 };
reed@google.com9987ec32011-09-07 11:57:52 +00002105 SkScalar dstX[4] = {
2106 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2107 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2108 };
2109 SkScalar dstY[4] = {
2110 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2111 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2112 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002113
reed@google.com9987ec32011-09-07 11:57:52 +00002114 if (dstX[1] > dstX[2]) {
2115 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2116 dstX[2] = dstX[1];
2117 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002118
reed@google.com9987ec32011-09-07 11:57:52 +00002119 if (dstY[1] > dstY[2]) {
2120 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2121 dstY[2] = dstY[1];
2122 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002123
reed@google.com9987ec32011-09-07 11:57:52 +00002124 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002125 SkRect s, d;
2126
reed@google.com9987ec32011-09-07 11:57:52 +00002127 s.fTop = srcY[y];
2128 s.fBottom = srcY[y+1];
2129 d.fTop = dstY[y];
2130 d.fBottom = dstY[y+1];
2131 for (int x = 0; x < 3; x++) {
2132 s.fLeft = srcX[x];
2133 s.fRight = srcX[x+1];
2134 d.fLeft = dstX[x];
2135 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002136 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002137 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002138 }
2139 }
2140}
2141
2142void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2143 const SkRect& dst, const SkPaint* paint) {
2144 SkDEBUGCODE(bitmap.validate();)
2145
2146 // Need a device entry-point, so gpu can use a mesh
2147 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2148}
2149
reed@google.comf67e4cf2011-03-15 20:56:58 +00002150class SkDeviceFilteredPaint {
2151public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002152 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2153 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002154 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002155 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002156 newPaint->setFlags(flags.fFlags);
2157 newPaint->setHinting(flags.fHinting);
2158 fPaint = newPaint;
2159 } else {
2160 fPaint = &paint;
2161 }
2162 }
2163
reed@google.comf67e4cf2011-03-15 20:56:58 +00002164 const SkPaint& paint() const { return *fPaint; }
2165
2166private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002167 const SkPaint* fPaint;
2168 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002169};
2170
bungeman@google.com52c748b2011-08-22 21:30:43 +00002171void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2172 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002173 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002174 draw.fDevice->drawRect(draw, r, paint);
2175 } else {
2176 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002177 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002178 draw.fDevice->drawRect(draw, r, p);
2179 }
2180}
2181
2182void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2183 const char text[], size_t byteLength,
2184 SkScalar x, SkScalar y) {
2185 SkASSERT(byteLength == 0 || text != NULL);
2186
2187 // nothing to draw
2188 if (text == NULL || byteLength == 0 ||
2189 draw.fClip->isEmpty() ||
2190 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2191 return;
2192 }
2193
2194 SkScalar width = 0;
2195 SkPoint start;
2196
2197 start.set(0, 0); // to avoid warning
2198 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2199 SkPaint::kStrikeThruText_Flag)) {
2200 width = paint.measureText(text, byteLength);
2201
2202 SkScalar offsetX = 0;
2203 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2204 offsetX = SkScalarHalf(width);
2205 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2206 offsetX = width;
2207 }
2208 start.set(x - offsetX, y);
2209 }
2210
2211 if (0 == width) {
2212 return;
2213 }
2214
2215 uint32_t flags = paint.getFlags();
2216
2217 if (flags & (SkPaint::kUnderlineText_Flag |
2218 SkPaint::kStrikeThruText_Flag)) {
2219 SkScalar textSize = paint.getTextSize();
2220 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2221 SkRect r;
2222
2223 r.fLeft = start.fX;
2224 r.fRight = start.fX + width;
2225
2226 if (flags & SkPaint::kUnderlineText_Flag) {
2227 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2228 start.fY);
2229 r.fTop = offset;
2230 r.fBottom = offset + height;
2231 DrawRect(draw, paint, r, textSize);
2232 }
2233 if (flags & SkPaint::kStrikeThruText_Flag) {
2234 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2235 start.fY);
2236 r.fTop = offset;
2237 r.fBottom = offset + height;
2238 DrawRect(draw, paint, r, textSize);
2239 }
2240 }
2241}
2242
reed@google.come0d9ce82014-04-23 04:00:17 +00002243void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2244 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002245 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246
2247 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002248 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002249 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002250 DrawTextDecorations(iter, dfp.paint(),
2251 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 }
2253
reed@google.com4e2b3d32011-04-07 14:18:59 +00002254 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255}
2256
reed@google.come0d9ce82014-04-23 04:00:17 +00002257void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2258 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002259 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002260
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002262 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002264 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002266
reed@google.com4e2b3d32011-04-07 14:18:59 +00002267 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268}
2269
reed@google.come0d9ce82014-04-23 04:00:17 +00002270void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2271 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002272 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002273
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002275 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002277 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002279
reed@google.com4e2b3d32011-04-07 14:18:59 +00002280 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281}
2282
reed@google.come0d9ce82014-04-23 04:00:17 +00002283void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2284 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002285 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002286
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 while (iter.next()) {
2288 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002289 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002291
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002292 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002293}
2294
reed@google.come0d9ce82014-04-23 04:00:17 +00002295// These will become non-virtual, so they always call the (virtual) onDraw... method
2296void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2297 const SkPaint& paint) {
2298 this->onDrawText(text, byteLength, x, y, paint);
2299}
2300void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2301 const SkPaint& paint) {
2302 this->onDrawPosText(text, byteLength, pos, paint);
2303}
2304void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2305 SkScalar constY, const SkPaint& paint) {
2306 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2307}
2308void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2309 const SkMatrix* matrix, const SkPaint& paint) {
2310 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2311}
2312
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2314 const SkPoint verts[], const SkPoint texs[],
2315 const SkColor colors[], SkXfermode* xmode,
2316 const uint16_t indices[], int indexCount,
2317 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002318 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002319
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 while (iter.next()) {
2321 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002322 colors, xmode, indices, indexCount,
2323 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002324 }
reed@google.com4b226022011-01-11 18:32:13 +00002325
reed@google.com4e2b3d32011-04-07 14:18:59 +00002326 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002327}
2328
2329//////////////////////////////////////////////////////////////////////////////
2330// These methods are NOT virtual, and therefore must call back into virtual
2331// methods, rather than actually drawing themselves.
2332//////////////////////////////////////////////////////////////////////////////
2333
2334void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002335 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 SkPaint paint;
2337
2338 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002339 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002340 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002341 }
2342 this->drawPaint(paint);
2343}
2344
reed@android.com845fdac2009-06-23 03:01:32 +00002345void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002346 SkPaint paint;
2347
2348 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002349 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002350 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 }
2352 this->drawPaint(paint);
2353}
2354
2355void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2356 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002357
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358 pt.set(x, y);
2359 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2360}
2361
2362void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2363 SkPoint pt;
2364 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002365
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 pt.set(x, y);
2367 paint.setColor(color);
2368 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2369}
2370
2371void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2372 const SkPaint& paint) {
2373 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002374
reed@android.com8a1c16f2008-12-17 15:59:43 +00002375 pts[0].set(x0, y0);
2376 pts[1].set(x1, y1);
2377 this->drawPoints(kLines_PointMode, 2, pts, paint);
2378}
2379
2380void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2381 SkScalar right, SkScalar bottom,
2382 const SkPaint& paint) {
2383 SkRect r;
2384
2385 r.set(left, top, right, bottom);
2386 this->drawRect(r, paint);
2387}
2388
2389void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2390 const SkPaint& paint) {
2391 if (radius < 0) {
2392 radius = 0;
2393 }
2394
2395 SkRect r;
2396 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002397 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398}
2399
2400void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2401 const SkPaint& paint) {
2402 if (rx > 0 && ry > 0) {
2403 if (paint.canComputeFastBounds()) {
2404 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002405 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002406 return;
2407 }
2408 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002409 SkRRect rrect;
2410 rrect.setRectXY(r, rx, ry);
2411 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412 } else {
2413 this->drawRect(r, paint);
2414 }
2415}
2416
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2418 SkScalar sweepAngle, bool useCenter,
2419 const SkPaint& paint) {
2420 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2421 this->drawOval(oval, paint);
2422 } else {
2423 SkPath path;
2424 if (useCenter) {
2425 path.moveTo(oval.centerX(), oval.centerY());
2426 }
2427 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2428 if (useCenter) {
2429 path.close();
2430 }
2431 this->drawPath(path, paint);
2432 }
2433}
2434
2435void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2436 const SkPath& path, SkScalar hOffset,
2437 SkScalar vOffset, const SkPaint& paint) {
2438 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002439
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440 matrix.setTranslate(hOffset, vOffset);
2441 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2442}
2443
reed@android.comf76bacf2009-05-13 14:00:33 +00002444///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002445void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002446 SkBaseDevice* device = this->getDevice();
2447 if (NULL != device) {
2448 device->EXPERIMENTAL_optimize(picture);
2449 }
2450}
reed@android.comf76bacf2009-05-13 14:00:33 +00002451
robertphillips9b14f262014-06-04 05:40:44 -07002452void SkCanvas::EXPERIMENTAL_purge(const SkPicture* picture) {
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00002453 SkBaseDevice* device = this->getTopDevice();
2454 if (NULL != device) {
2455 device->EXPERIMENTAL_purge(picture);
2456 }
2457}
2458
robertphillips9b14f262014-06-04 05:40:44 -07002459void SkCanvas::drawPicture(const SkPicture* picture) {
2460 if (NULL != picture) {
2461 this->onDrawPicture(picture);
2462 }
2463}
2464
2465void SkCanvas::onDrawPicture(const SkPicture* picture) {
2466 SkASSERT(NULL != picture);
2467
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002468 SkBaseDevice* device = this->getTopDevice();
2469 if (NULL != device) {
2470 // Canvas has to first give the device the opportunity to render
2471 // the picture itself.
robertphillips9b14f262014-06-04 05:40:44 -07002472 if (device->EXPERIMENTAL_drawPicture(this, picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002473 return; // the device has rendered the entire picture
2474 }
2475 }
2476
robertphillips9b14f262014-06-04 05:40:44 -07002477 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478}
2479
reed@android.com8a1c16f2008-12-17 15:59:43 +00002480///////////////////////////////////////////////////////////////////////////////
2481///////////////////////////////////////////////////////////////////////////////
2482
2483SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002484 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002485
2486 SkASSERT(canvas);
2487
2488 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2489 fDone = !fImpl->next();
2490}
2491
2492SkCanvas::LayerIter::~LayerIter() {
2493 fImpl->~SkDrawIter();
2494}
2495
2496void SkCanvas::LayerIter::next() {
2497 fDone = !fImpl->next();
2498}
2499
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002500SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002501 return fImpl->getDevice();
2502}
2503
2504const SkMatrix& SkCanvas::LayerIter::matrix() const {
2505 return fImpl->getMatrix();
2506}
2507
2508const SkPaint& SkCanvas::LayerIter::paint() const {
2509 const SkPaint* paint = fImpl->getPaint();
2510 if (NULL == paint) {
2511 paint = &fDefaultPaint;
2512 }
2513 return *paint;
2514}
2515
2516const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2517int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2518int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002519
2520///////////////////////////////////////////////////////////////////////////////
2521
fmalitac3b589a2014-06-05 12:40:07 -07002522SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002523
2524///////////////////////////////////////////////////////////////////////////////
2525
2526static bool supported_for_raster_canvas(const SkImageInfo& info) {
2527 switch (info.alphaType()) {
2528 case kPremul_SkAlphaType:
2529 case kOpaque_SkAlphaType:
2530 break;
2531 default:
2532 return false;
2533 }
2534
2535 switch (info.colorType()) {
2536 case kAlpha_8_SkColorType:
2537 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002538 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002539 break;
2540 default:
2541 return false;
2542 }
2543
2544 return true;
2545}
2546
2547SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2548 if (!supported_for_raster_canvas(info)) {
2549 return NULL;
2550 }
2551
2552 SkBitmap bitmap;
2553 if (!bitmap.allocPixels(info)) {
2554 return NULL;
2555 }
2556
2557 // should this functionality be moved into allocPixels()?
2558 if (!bitmap.info().isOpaque()) {
2559 bitmap.eraseColor(0);
2560 }
2561 return SkNEW_ARGS(SkCanvas, (bitmap));
2562}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002563
2564SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2565 if (!supported_for_raster_canvas(info)) {
2566 return NULL;
2567 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002568
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002569 SkBitmap bitmap;
2570 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2571 return NULL;
2572 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002573 return SkNEW_ARGS(SkCanvas, (bitmap));
2574}