blob: ead844fcc1ed7df4c36d10d2c168949ee6d2d5d4 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
8#include "SkCanvas.h"
reed6a070dc2014-11-11 19:36:09 -08009#include "SkCanvasDrawable.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000012#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
piotaixrb5fae932014-09-24 13:03:30 -070016#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070019#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000021#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000022#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000023#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000024#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070026#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000027#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000028#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080029#include "SkTraceEvent.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000032#if SK_SUPPORT_GPU
33#include "GrRenderTarget.h"
34#endif
35
reed@google.comda17f752012-08-16 18:27:05 +000036// experimental for faster tiled drawing...
37//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000038
reed@android.com8a1c16f2008-12-17 15:59:43 +000039//#define SK_TRACE_SAVERESTORE
40
41#ifdef SK_TRACE_SAVERESTORE
42 static int gLayerCounter;
43 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
44 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
45
46 static int gRecCounter;
47 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
48 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
49
50 static int gCanvasCounter;
51 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
52 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
53#else
54 #define inc_layer()
55 #define dec_layer()
56 #define inc_rec()
57 #define dec_rec()
58 #define inc_canvas()
59 #define dec_canvas()
60#endif
61
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000062typedef SkTLazy<SkPaint> SkLazyPaint;
63
reed@google.com97af1a62012-08-28 12:19:02 +000064void SkCanvas::predrawNotify() {
65 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000066 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000067 }
68}
69
reed@android.com8a1c16f2008-12-17 15:59:43 +000070///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000071
reed4a8126e2014-09-22 07:29:03 -070072static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
73 const uint32_t propFlags = props.flags();
74 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
75 flags &= ~SkPaint::kDither_Flag;
76 }
77 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
78 flags &= ~SkPaint::kAntiAlias_Flag;
79 }
80 return flags;
81}
82
83///////////////////////////////////////////////////////////////////////////////
84
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000085/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 The clip/matrix/proc are fields that reflect the top of the save/restore
87 stack. Whenever the canvas changes, it marks a dirty flag, and then before
88 these are used (assuming we're not on a layer) we rebuild these cache
89 values: they reflect the top of the save stack, but translated and clipped
90 by the device's XY offset and bitmap-bounds.
91*/
92struct DeviceCM {
93 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000094 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000095 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000097 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000098
reedd9544982014-09-09 18:46:22 -070099 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
100 bool conservativeRasterClip)
101 : fNext(NULL)
102 , fClip(conservativeRasterClip)
103 {
104 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000106 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 }
reed@google.com4b226022011-01-11 18:32:13 +0000108 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000112 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700113 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000114 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 fDevice->unref();
116 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000117 SkDELETE(fPaint);
118 }
reed@google.com4b226022011-01-11 18:32:13 +0000119
reed@google.com045e62d2011-10-24 12:19:46 +0000120 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
121 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000122 int x = fDevice->getOrigin().x();
123 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 int width = fDevice->width();
125 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000126
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 if ((x | y) == 0) {
128 fMatrix = &totalMatrix;
129 fClip = totalClip;
130 } else {
131 fMatrixStorage = totalMatrix;
132 fMatrixStorage.postTranslate(SkIntToScalar(-x),
133 SkIntToScalar(-y));
134 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000135
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 totalClip.translate(-x, -y, &fClip);
137 }
138
reed@google.com045e62d2011-10-24 12:19:46 +0000139 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
141 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000144 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 SkRegion::kDifference_Op);
146 }
reed@google.com4b226022011-01-11 18:32:13 +0000147
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000148 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150#ifdef SK_DEBUG
151 if (!fClip.isEmpty()) {
152 SkIRect deviceR;
153 deviceR.set(0, 0, width, height);
154 SkASSERT(deviceR.contains(fClip.getBounds()));
155 }
156#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000157 }
158
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000160 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161};
162
163/* This is the record we keep for each save/restore level in the stack.
164 Since a level optionally copies the matrix and/or stack, we have pointers
165 for these fields. If the value is copied for this level, the copy is
166 stored in the ...Storage field, and the pointer points to that. If the
167 value is not copied for this level, we ignore ...Storage, and just point
168 at the corresponding value in the previous level in the stack.
169*/
170class SkCanvas::MCRec {
171public:
reed6f097092014-09-09 12:51:10 -0700172 SkRasterClip fRasterClip;
reedd9544982014-09-09 18:46:22 -0700173 SkMatrix fMatrix;
reed1f836ee2014-07-07 07:49:34 -0700174 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700175 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 /* If there are any layers in the stack, this points to the top-most
177 one that is at or below this level in the stack (so we know what
178 bitmap/device to draw into from this level. This value is NOT
179 reference counted, since the real owner is either our fLayer field,
180 or a previous one in a lower level.)
181 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000182 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
reedd9544982014-09-09 18:46:22 -0700184 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
185 fMatrix.reset();
186 fFilter = NULL;
187 fLayer = NULL;
188 fTopLayer = NULL;
piotaixrb5fae932014-09-24 13:03:30 -0700189
reedd9544982014-09-09 18:46:22 -0700190 // don't bother initializing fNext
191 inc_rec();
192 }
193 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip) {
194 fMatrix = prev.fMatrix;
195 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700197 fTopLayer = prev.fTopLayer;
piotaixrb5fae932014-09-24 13:03:30 -0700198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 // don't bother initializing fNext
200 inc_rec();
201 }
202 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000203 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 SkDELETE(fLayer);
205 dec_rec();
206 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207};
208
209class SkDrawIter : public SkDraw {
210public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000211 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000212 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000213 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 canvas->updateDeviceCMCache();
215
reed@google.com90c07ea2012-04-13 13:50:27 +0000216 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000218 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 }
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 bool next() {
222 // skip over recs with empty clips
223 if (fSkipEmptyClips) {
224 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
225 fCurrLayer = fCurrLayer->fNext;
226 }
227 }
228
reed@google.comf68c5e22012-02-24 16:38:58 +0000229 const DeviceCM* rec = fCurrLayer;
230 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231
232 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000233 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
234 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 fDevice = rec->fDevice;
236 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000238 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239
240 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 return true;
244 }
245 return false;
246 }
reed@google.com4b226022011-01-11 18:32:13 +0000247
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000248 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000249 int getX() const { return fDevice->getOrigin().x(); }
250 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 const SkMatrix& getMatrix() const { return *fMatrix; }
252 const SkRegion& getClip() const { return *fClip; }
253 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000254
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255private:
256 SkCanvas* fCanvas;
257 const DeviceCM* fCurrLayer;
258 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 SkBool8 fSkipEmptyClips;
260
261 typedef SkDraw INHERITED;
262};
263
264/////////////////////////////////////////////////////////////////////////////
265
266class AutoDrawLooper {
267public:
reed4a8126e2014-09-22 07:29:03 -0700268 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000269 bool skipLayerForImageFilter = false,
270 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000271 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700273 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000274 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000275 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000276 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277
reed@google.com8926b162012-03-23 15:36:36 +0000278 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
279 SkPaint tmp;
280 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000281 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
282 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000283 // we'll clear the imageFilter for the actual draws in next(), so
284 // it will only be applied during the restore().
285 fDoClearImageFilter = true;
286 }
287
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000288 if (SkDrawLooper* looper = paint.getLooper()) {
289 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
290 looper->contextSize());
291 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000292 fIsSimple = false;
293 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000294 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000295 // can we be marked as simple?
296 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000297 }
piotaixrb5fae932014-09-24 13:03:30 -0700298
reed4a8126e2014-09-22 07:29:03 -0700299 uint32_t oldFlags = paint.getFlags();
300 fNewPaintFlags = filter_paint_flags(props, oldFlags);
301 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
302 SkPaint* paint = fLazyPaint.set(fOrigPaint);
303 paint->setFlags(fNewPaintFlags);
304 fPaint = paint;
305 // if we're not simple, doNext() will take care of calling setFlags()
306 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000307 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000308
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000310 if (fDoClearImageFilter) {
311 fCanvas->internalRestore();
312 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000315
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 const SkPaint& paint() const {
317 SkASSERT(fPaint);
318 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000320
reed@google.com129ec222012-05-15 13:24:09 +0000321 bool next(SkDrawFilter::Type drawType) {
322 if (fDone) {
323 return false;
324 } else if (fIsSimple) {
325 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000326 return !fPaint->nothingToDraw();
327 } else {
328 return this->doNext(drawType);
329 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000330 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000331
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000333 SkLazyPaint fLazyPaint;
334 SkCanvas* fCanvas;
335 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000336 SkDrawFilter* fFilter;
337 const SkPaint* fPaint;
338 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700339 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000340 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000341 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000342 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000343 SkDrawLooper::Context* fLooperContext;
344 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000345
346 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347};
348
reed@google.com129ec222012-05-15 13:24:09 +0000349bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000350 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000351 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000352 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000353
354 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700355 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000356
357 if (fDoClearImageFilter) {
358 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000360
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000361 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000362 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000363 return false;
364 }
365 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000366 if (!fFilter->filter(paint, drawType)) {
367 fDone = true;
368 return false;
369 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000370 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000371 // no looper means we only draw once
372 fDone = true;
373 }
374 }
375 fPaint = paint;
376
377 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000378 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000379 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000380 }
381
382 // call this after any possible paint modifiers
383 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000384 fPaint = NULL;
385 return false;
386 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000387 return true;
388}
389
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390////////// macros to place around the internal draw calls //////////////////
391
reed@google.com8926b162012-03-23 15:36:36 +0000392#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000393 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700394 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000395 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000396 SkDrawIter iter(this);
397
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000398#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000399 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700400 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000401 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000403
reed@google.com4e2b3d32011-04-07 14:18:59 +0000404#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405
406////////////////////////////////////////////////////////////////////////////
407
reed4a8126e2014-09-22 07:29:03 -0700408void SkCanvas::setupDevice(SkBaseDevice* device) {
409 device->setPixelGeometry(fProps.pixelGeometry());
410}
411
reedd9544982014-09-09 18:46:22 -0700412SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
413 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000414 fCachedLocalClipBounds.setEmpty();
415 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000416 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000417 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700418 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000419 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000420 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000421 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422
423 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700424 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425
reedd9544982014-09-09 18:46:22 -0700426 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428
reed@google.com97af1a62012-08-28 12:19:02 +0000429 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000430
reedf92c8662014-08-18 08:02:43 -0700431 if (device) {
reed4a8126e2014-09-22 07:29:03 -0700432 this->setupDevice(device);
433 if (device->forceConservativeRasterClip()) {
434 fConservativeRasterClip = true;
435 }
reedf92c8662014-08-18 08:02:43 -0700436 device->onAttachToCanvas(this);
437 fMCRec->fLayer->fDevice = SkRef(device);
438 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
439 }
440 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441}
442
reed@google.comcde92112011-07-06 20:00:52 +0000443SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000444 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700445 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000446{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000447 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000448
reedd9544982014-09-09 18:46:22 -0700449 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000450}
451
reedd9544982014-09-09 18:46:22 -0700452static SkBitmap make_nopixels(int width, int height) {
453 SkBitmap bitmap;
454 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
455 return bitmap;
456}
457
458class SkNoPixelsBitmapDevice : public SkBitmapDevice {
459public:
460 SkNoPixelsBitmapDevice(int width, int height) : INHERITED(make_nopixels(width, height)) {}
461
462private:
piotaixrb5fae932014-09-24 13:03:30 -0700463
reedd9544982014-09-09 18:46:22 -0700464 typedef SkBitmapDevice INHERITED;
465};
466
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000467SkCanvas::SkCanvas(int width, int height)
468 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700469 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000470{
471 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700472
reedd9544982014-09-09 18:46:22 -0700473 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), kDefault_InitFlags)->unref();
474}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000475
reedd9544982014-09-09 18:46:22 -0700476SkCanvas::SkCanvas(int width, int height, InitFlags flags)
477 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700478 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700479{
480 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700481
reedd9544982014-09-09 18:46:22 -0700482 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), flags)->unref();
483}
484
reed4a8126e2014-09-22 07:29:03 -0700485SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700486 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700487 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700488{
489 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700490
reedd9544982014-09-09 18:46:22 -0700491 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000492}
493
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000494SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000495 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700496 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000497{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700499
reedd9544982014-09-09 18:46:22 -0700500 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501}
502
reed4a8126e2014-09-22 07:29:03 -0700503SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700504 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700505 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700506{
507 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700508
reed4a8126e2014-09-22 07:29:03 -0700509 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
510 this->init(device, kDefault_InitFlags);
511}
reed29c857d2014-09-21 10:25:07 -0700512
reed4a8126e2014-09-22 07:29:03 -0700513SkCanvas::SkCanvas(const SkBitmap& bitmap)
514 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
515 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
516{
517 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700518
reed4a8126e2014-09-22 07:29:03 -0700519 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
520 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521}
522
523SkCanvas::~SkCanvas() {
524 // free up the contents of our deque
525 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000526 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000527
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 this->internalRestore(); // restore the last, since we're going away
529
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000530 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 dec_canvas();
533}
534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535SkDrawFilter* SkCanvas::getDrawFilter() const {
536 return fMCRec->fFilter;
537}
538
539SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
540 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
541 return filter;
542}
543
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000544SkMetaData& SkCanvas::getMetaData() {
545 // metadata users are rare, so we lazily allocate it. If that changes we
546 // can decide to just make it a field in the device (rather than a ptr)
547 if (NULL == fMetaData) {
548 fMetaData = new SkMetaData;
549 }
550 return *fMetaData;
551}
552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553///////////////////////////////////////////////////////////////////////////////
554
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000555void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000556 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000557 if (device) {
558 device->flush();
559 }
560}
561
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000562SkISize SkCanvas::getTopLayerSize() const {
563 SkBaseDevice* d = this->getTopDevice();
564 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
565}
566
567SkIPoint SkCanvas::getTopLayerOrigin() const {
568 SkBaseDevice* d = this->getTopDevice();
569 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
570}
571
572SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000573 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000574 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
575}
576
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000577SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000579 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 SkASSERT(rec && rec->fLayer);
581 return rec->fLayer->fDevice;
582}
583
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000584SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000585 if (updateMatrixClip) {
586 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
587 }
reed@google.com9266fed2011-03-30 00:18:03 +0000588 return fMCRec->fTopLayer->fDevice;
589}
590
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000591SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000593 SkDeque::F2BIter iter(fMCStack);
594 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000596 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597
598 if (rootDevice == device) {
599 return device;
600 }
reed@google.com4b226022011-01-11 18:32:13 +0000601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000603 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 }
605 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000606 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 }
608
609 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
610 rootDevice = device;
reed4a8126e2014-09-22 07:29:03 -0700611 this->setupDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612
613 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 /* Now we update our initial region to have the bounds of the new device,
616 and then intersect all of the clips in our stack with these bounds,
617 to ensure that we can't draw outside of the device's bounds (and trash
618 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 NOTE: this is only a partial-fix, since if the new device is larger than
621 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000622 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
624 reconstruct the correct clips, so this approximation will have to do.
625 The caller really needs to restore() back to the base if they want to
626 accurately take advantage of the new device bounds.
627 */
628
reed@google.com42aea282012-03-28 16:19:15 +0000629 SkIRect bounds;
630 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000632 } else {
633 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
reed@google.com42aea282012-03-28 16:19:15 +0000635 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700636 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000637 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700638 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000639 }
640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 return device;
642}
643
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000644bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
645 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
646 return false;
647 }
648
649 bool weAllocated = false;
650 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700651 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000652 return false;
653 }
654 weAllocated = true;
655 }
656
657 SkBitmap bm(*bitmap);
658 bm.lockPixels();
659 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
660 return true;
661 }
662
663 if (weAllocated) {
664 bitmap->setPixelRef(NULL);
665 }
666 return false;
667}
reed@google.com51df9e32010-12-23 19:29:18 +0000668
bsalomon@google.comc6980972011-11-02 19:57:21 +0000669bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000670 SkIRect r = srcRect;
671 const SkISize size = this->getBaseLayerSize();
672 if (!r.intersect(0, 0, size.width(), size.height())) {
673 bitmap->reset();
674 return false;
675 }
676
reed84825042014-09-02 12:50:45 -0700677 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000678 // bitmap will already be reset.
679 return false;
680 }
681 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
682 bitmap->reset();
683 return false;
684 }
685 return true;
686}
687
688bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
689 switch (origInfo.colorType()) {
690 case kUnknown_SkColorType:
691 case kIndex_8_SkColorType:
692 return false;
693 default:
694 break;
695 }
696 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
697 return false;
698 }
699 if (0 == origInfo.width() || 0 == origInfo.height()) {
700 return false;
701 }
702
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000703 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000704 if (!device) {
705 return false;
706 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000707
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000708 const SkISize size = this->getBaseLayerSize();
709 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
710 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000711 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000712 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000713
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000714 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700715 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000716
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000717 // if x or y are negative, then we have to adjust pixels
718 if (x > 0) {
719 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000720 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000721 if (y > 0) {
722 y = 0;
723 }
724 // here x,y are either 0 or negative
725 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000726
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000727 // The device can assert that the requested area is always contained in its bounds
728 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000729}
730
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000731bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
732 if (bitmap.getTexture()) {
733 return false;
734 }
735 SkBitmap bm(bitmap);
736 bm.lockPixels();
737 if (bm.getPixels()) {
738 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
739 }
740 return false;
741}
742
743bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
744 int x, int y) {
745 switch (origInfo.colorType()) {
746 case kUnknown_SkColorType:
747 case kIndex_8_SkColorType:
748 return false;
749 default:
750 break;
751 }
752 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
753 return false;
754 }
755
756 const SkISize size = this->getBaseLayerSize();
757 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
758 if (!target.intersect(0, 0, size.width(), size.height())) {
759 return false;
760 }
761
762 SkBaseDevice* device = this->getDevice();
763 if (!device) {
764 return false;
765 }
766
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000767 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700768 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000769
770 // if x or y are negative, then we have to adjust pixels
771 if (x > 0) {
772 x = 0;
773 }
774 if (y > 0) {
775 y = 0;
776 }
777 // here x,y are either 0 or negative
778 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
779
reed4af35f32014-06-27 17:47:49 -0700780 // Tell our owning surface to bump its generation ID
781 this->predrawNotify();
782
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000783 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000784 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000785}
reed@google.com51df9e32010-12-23 19:29:18 +0000786
junov@google.com4370aed2012-01-18 16:21:08 +0000787SkCanvas* SkCanvas::canvasForDrawIter() {
788 return this;
789}
790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791//////////////////////////////////////////////////////////////////////////////
792
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793void SkCanvas::updateDeviceCMCache() {
794 if (fDeviceCMDirty) {
795 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700796 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000798
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000800 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000802 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000804 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 } while ((layer = layer->fNext) != NULL);
806 }
807 fDeviceCMDirty = false;
808 }
809}
810
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811///////////////////////////////////////////////////////////////////////////////
812
Florin Malita5f6102d2014-06-30 10:13:28 -0400813int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000815
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700817 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000819
Florin Malita5f6102d2014-06-30 10:13:28 -0400820 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000821
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 return saveCount;
823}
824
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000825int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400826 this->willSave();
827 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828}
829
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000831#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000833#else
834 return true;
835#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836}
837
junov@chromium.orga907ac32012-02-24 21:54:07 +0000838bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000839 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000840 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000841 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000842 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000843 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000844 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000845
846 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700847 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000848 // Filters may grow the bounds beyond the device bounds.
849 op = SkRegion::kReplace_Op;
850 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000851 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700852 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000854
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 this->getTotalMatrix().mapRect(&r, *bounds);
856 r.roundOut(&ir);
857 // early exit if the layer's bounds are clipped out
858 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000859 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700860 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000861 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000862 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 }
864 } else { // no user bounds, so just use the clip
865 ir = clipBounds;
866 }
867
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000868 if (bounds_affects_clip(flags)) {
869 fClipStack.clipDevRect(ir, op);
870 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700871 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000872 return false;
873 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000874 }
875
876 if (intersection) {
877 *intersection = ir;
878 }
879 return true;
880}
881
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000882int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
883 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
884 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
885}
886
junov@chromium.orga907ac32012-02-24 21:54:07 +0000887int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
888 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000889 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
890 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000891}
892
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000893int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
894 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000895#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000896 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000897#endif
898
junov@chromium.orga907ac32012-02-24 21:54:07 +0000899 // do this before we create the layer. We don't call the public save() since
900 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400901 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000902
903 fDeviceCMDirty = true;
904
905 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000906 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 return count;
908 }
909
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000910 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
911 // the clipRectBounds() call above?
912 if (kNoLayer_SaveLayerStrategy == strategy) {
913 return count;
914 }
915
reed@google.comb55deeb2012-01-06 14:43:09 +0000916 // Kill the imagefilter if our device doesn't allow it
917 SkLazyPaint lazyP;
918 if (paint && paint->getImageFilter()) {
919 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000920 if (justForImageFilter) {
921 // early exit if the layer was just for the imageFilter
922 return count;
923 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000924 SkPaint* p = lazyP.set(*paint);
925 p->setImageFilter(NULL);
926 paint = p;
927 }
928 }
929
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000930 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
931 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
932 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000934 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000935 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700936 device = this->getDevice();
937 if (device) {
senorblancob0e89dc2014-10-20 14:03:12 -0700938 device = device->createCompatibleDeviceForImageFilter(info);
reed52d9ac62014-06-30 09:05:34 -0700939 }
reed@google.com76dd2772012-01-05 21:15:07 +0000940 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000941 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000942 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000943 if (NULL == device) {
944 SkDebugf("Unable to create device for layer.");
945 return count;
946 }
reed4a8126e2014-09-22 07:29:03 -0700947 this->setupDevice(device);
bsalomon@google.come97f0852011-06-17 13:10:25 +0000948
reed@google.com6f8f2922011-03-04 22:27:10 +0000949 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700950 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
951 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 device->unref();
953
954 layer->fNext = fMCRec->fTopLayer;
955 fMCRec->fLayer = layer;
956 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
957
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000958 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 return count;
960}
961
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000962int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
963 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
964}
965
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
967 SaveFlags flags) {
968 if (0xFF == alpha) {
969 return this->saveLayer(bounds, NULL, flags);
970 } else {
971 SkPaint tmpPaint;
972 tmpPaint.setAlpha(alpha);
973 return this->saveLayer(bounds, &tmpPaint, flags);
974 }
975}
976
977void SkCanvas::restore() {
978 // check for underflow
979 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000980 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700982 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 }
984}
985
986void SkCanvas::internalRestore() {
987 SkASSERT(fMCStack.count() != 0);
988
989 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000990 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991
Florin Malita5f6102d2014-06-30 10:13:28 -0400992 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000993
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000994 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995 DeviceCM* layer = fMCRec->fLayer; // may be null
996 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
997 fMCRec->fLayer = NULL;
998
999 // now do the normal restore()
1000 fMCRec->~MCRec(); // balanced in save()
1001 fMCStack.pop_back();
1002 fMCRec = (MCRec*)fMCStack.back();
1003
1004 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1005 since if we're being recorded, we don't want to record this (the
1006 recorder will have already recorded the restore).
1007 */
bsalomon49f085d2014-09-05 13:34:00 -07001008 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001010 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001011 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1012 layer->fPaint);
1013 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001015
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001016 SkASSERT(fSaveLayerCount > 0);
1017 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 }
1019 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001020 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021}
1022
1023int SkCanvas::getSaveCount() const {
1024 return fMCStack.count();
1025}
1026
1027void SkCanvas::restoreToCount(int count) {
1028 // sanity check
1029 if (count < 1) {
1030 count = 1;
1031 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001032
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001033 int n = this->getSaveCount() - count;
1034 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 this->restore();
1036 }
1037}
1038
reed@google.com7c202932011-12-14 18:48:05 +00001039bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001040 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001041}
1042
reed4a8126e2014-09-22 07:29:03 -07001043SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1044 if (NULL == props) {
1045 props = &fProps;
1046 }
1047 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001048}
1049
reed4a8126e2014-09-22 07:29:03 -07001050SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001051 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001052 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001053}
1054
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001055SkImageInfo SkCanvas::imageInfo() const {
1056 SkBaseDevice* dev = this->getDevice();
1057 if (dev) {
1058 return dev->imageInfo();
1059 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001060 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001061 }
1062}
1063
1064const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1065 return this->onPeekPixels(info, rowBytes);
1066}
1067
1068const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1069 SkBaseDevice* dev = this->getDevice();
1070 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1071}
1072
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001073void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1074 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1075 if (pixels && origin) {
1076 *origin = this->getTopDevice(false)->getOrigin();
1077 }
1078 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001079}
1080
1081void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1082 SkBaseDevice* dev = this->getTopDevice();
1083 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1084}
1085
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001086SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1087 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1088 if (NULL == fAddr) {
1089 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001090 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001091 return; // failure, fAddr is NULL
1092 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001093 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1094 return; // failure, fAddr is NULL
1095 }
1096 fAddr = fBitmap.getPixels();
1097 fRowBytes = fBitmap.rowBytes();
1098 }
1099 SkASSERT(fAddr); // success
1100}
1101
1102bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1103 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001104 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001105 } else {
1106 bitmap->reset();
1107 return false;
1108 }
1109}
1110
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001111void SkCanvas::onPushCull(const SkRect& cullRect) {
1112 // do nothing. Subclasses may do something
1113}
1114
1115void SkCanvas::onPopCull() {
1116 // do nothing. Subclasses may do something
1117}
1118
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001120#ifdef SK_DEBUG
1121// Ensure that cull rects are monotonically nested in device space.
1122void SkCanvas::validateCull(const SkIRect& devCull) {
1123 if (fCullStack.isEmpty()
1124 || devCull.isEmpty()
1125 || fCullStack.top().contains(devCull)) {
1126 return;
1127 }
1128
1129 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1130 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1131 fCullStack.top().x(), fCullStack.top().y(),
1132 fCullStack.top().right(), fCullStack.top().bottom()));
1133
1134#ifdef ASSERT_NESTED_CULLING
1135 SkDEBUGFAIL("Invalid cull.");
1136#endif
1137}
1138#endif
1139
1140void SkCanvas::pushCull(const SkRect& cullRect) {
1141 ++fCullCount;
1142 this->onPushCull(cullRect);
1143
1144#ifdef SK_DEBUG
1145 // Map the cull rect into device space.
1146 SkRect mappedCull;
1147 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1148
1149 // Take clipping into account.
1150 SkIRect devClip, devCull;
1151 mappedCull.roundOut(&devCull);
1152 this->getClipDeviceBounds(&devClip);
1153 if (!devCull.intersect(devClip)) {
1154 devCull.setEmpty();
1155 }
1156
1157 this->validateCull(devCull);
1158 fCullStack.push(devCull); // balanced in popCull
1159#endif
1160}
1161
1162void SkCanvas::popCull() {
1163 SkASSERT(fCullStack.count() == fCullCount);
1164
1165 if (fCullCount > 0) {
1166 --fCullCount;
1167 this->onPopCull();
1168
1169 SkDEBUGCODE(fCullStack.pop());
1170 }
1171}
1172
1173/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001175void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001177 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 return;
1179 }
1180
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001181 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001183 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001185
1186 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001187
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001188 SkRect storage;
1189 const SkRect* bounds = NULL;
1190 if (paint && paint->canComputeFastBounds()) {
1191 bitmap.getBounds(&storage);
1192 matrix.mapRect(&storage);
1193 bounds = &paint->computeFastBounds(storage, &storage);
1194 }
1195
1196 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001197
1198 while (iter.next()) {
1199 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1200 }
1201
1202 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203}
1204
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001205void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001206 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 SkPaint tmp;
1208 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 paint = &tmp;
1210 }
reed@google.com4b226022011-01-11 18:32:13 +00001211
reed@google.com8926b162012-03-23 15:36:36 +00001212 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001214 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001215 paint = &looper.paint();
1216 SkImageFilter* filter = paint->getImageFilter();
1217 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001218 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001219 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001220 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001221 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001222 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001223 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001224 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001225 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001226 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001227 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001228 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001229 SkPaint tmpUnfiltered(*paint);
1230 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001231 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1232 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001233 }
1234 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001235 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001236 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001238 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239}
1240
reed@google.com8926b162012-03-23 15:36:36 +00001241void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1242 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001243 TRACE_EVENT0("skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001244 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001245 return;
1246 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001247 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001248
reed@google.com8926b162012-03-23 15:36:36 +00001249 SkPaint tmp;
1250 if (NULL == paint) {
1251 paint = &tmp;
1252 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001253
reed@google.com8926b162012-03-23 15:36:36 +00001254 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001255
reed@google.com8926b162012-03-23 15:36:36 +00001256 while (iter.next()) {
1257 paint = &looper.paint();
1258 SkImageFilter* filter = paint->getImageFilter();
1259 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1260 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001261 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001262 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001263 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001264 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001265 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001266 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001267 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001268 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001269 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001270 SkPaint tmpUnfiltered(*paint);
1271 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001272 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001273 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001274 }
1275 } else {
1276 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1277 }
1278 }
1279 LOOPER_END
1280}
1281
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001283void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001284 SkMatrix m;
1285 m.setTranslate(dx, dy);
1286 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287}
1288
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001289void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001290 SkMatrix m;
1291 m.setScale(sx, sy);
1292 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293}
1294
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001295void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001296 SkMatrix m;
1297 m.setRotate(degrees);
1298 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299}
1300
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001301void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001302 SkMatrix m;
1303 m.setSkew(sx, sy);
1304 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001305}
1306
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001307void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001308 if (matrix.isIdentity()) {
1309 return;
1310 }
1311
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001313 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001314 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001315
1316 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001317}
1318
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319void SkCanvas::setMatrix(const SkMatrix& matrix) {
1320 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001321 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001322 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001323 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324}
1325
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326void SkCanvas::resetMatrix() {
1327 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001328
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 matrix.reset();
1330 this->setMatrix(matrix);
1331}
1332
1333//////////////////////////////////////////////////////////////////////////////
1334
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001335void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1337 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001338}
1339
1340void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001341#ifdef SK_ENABLE_CLIP_QUICKREJECT
1342 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001343 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001344 return false;
1345 }
1346
reed@google.com3b3e8952012-08-16 20:53:31 +00001347 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001348 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001349 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001350
1351 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001352 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001353 }
1354 }
1355#endif
1356
reed@google.com5c3d1472011-02-22 19:12:23 +00001357 AutoValidateClip avc(this);
1358
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001360 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001361 if (!fAllowSoftClip) {
1362 edgeStyle = kHard_ClipEdgeStyle;
1363 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364
reed1f836ee2014-07-07 07:49:34 -07001365 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001366 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001367 // the matrix. This means we don't have to a) make a path, and b) tell
1368 // the region code to scan-convert the path, only to discover that it
1369 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371
reed1f836ee2014-07-07 07:49:34 -07001372 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001373 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001374 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001376 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001377 // and clip against that, since it can handle any matrix. However, to
1378 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1379 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 SkPath path;
1381
1382 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001383 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 }
1385}
1386
reed73e714e2014-09-04 09:02:23 -07001387static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1388 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001389 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001390}
1391
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001392void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001393 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001394 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001395 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1396 } else {
1397 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001398 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001399}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001400
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001401void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001402 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001403 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001404 AutoValidateClip avc(this);
1405
1406 fDeviceCMDirty = true;
1407 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001408 if (!fAllowSoftClip) {
1409 edgeStyle = kHard_ClipEdgeStyle;
1410 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001411
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001412 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001413
1414 SkPath devPath;
1415 devPath.addRRect(transformedRRect);
1416
reed73e714e2014-09-04 09:02:23 -07001417 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001418 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001419 }
1420
1421 SkPath path;
1422 path.addRRect(rrect);
1423 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001424 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001425}
1426
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001427void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1429 SkRect r;
1430 if (!path.isInverseFillType() && path.isRect(&r)) {
1431 this->onClipRect(r, op, edgeStyle);
1432 } else {
1433 this->onClipPath(path, op, edgeStyle);
1434 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001435}
1436
1437void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001438#ifdef SK_ENABLE_CLIP_QUICKREJECT
1439 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001440 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001441 return false;
1442 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001443
reed@google.com3b3e8952012-08-16 20:53:31 +00001444 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001445 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001446 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001447
reed@google.comda17f752012-08-16 18:27:05 +00001448 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001449 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001450 }
1451 }
1452#endif
1453
reed@google.com5c3d1472011-02-22 19:12:23 +00001454 AutoValidateClip avc(this);
1455
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001457 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001458 if (!fAllowSoftClip) {
1459 edgeStyle = kHard_ClipEdgeStyle;
1460 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461
1462 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001463 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464
reed@google.comfe701122011-11-08 19:41:23 +00001465 // Check if the transfomation, or the original path itself
1466 // made us empty. Note this can also happen if we contained NaN
1467 // values. computing the bounds detects this, and will set our
1468 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1469 if (devPath.getBounds().isEmpty()) {
1470 // resetting the path will remove any NaN or other wanky values
1471 // that might upset our scan converter.
1472 devPath.reset();
1473 }
1474
reed@google.com5c3d1472011-02-22 19:12:23 +00001475 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001476 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001477
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001478 if (fAllowSimplifyClip) {
1479 devPath.reset();
1480 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1481 const SkClipStack* clipStack = getClipStack();
1482 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1483 const SkClipStack::Element* element;
1484 while ((element = iter.next())) {
1485 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001486 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001487 if (type != SkClipStack::Element::kEmpty_Type) {
1488 element->asPath(&operand);
1489 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001490 SkRegion::Op elementOp = element->getOp();
1491 if (elementOp == SkRegion::kReplace_Op) {
1492 devPath = operand;
1493 } else {
1494 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1495 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001496 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1497 // perhaps we need an API change to avoid this sort of mixed-signals about
1498 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001499 if (element->isAA()) {
1500 edgeStyle = kSoft_ClipEdgeStyle;
1501 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001502 }
1503 op = SkRegion::kReplace_Op;
1504 }
1505
reed73e714e2014-09-04 09:02:23 -07001506 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507}
1508
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001509void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001510 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001511}
1512
1513void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001514 AutoValidateClip avc(this);
1515
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001517 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518
reed@google.com5c3d1472011-02-22 19:12:23 +00001519 // todo: signal fClipStack that we have a region, and therefore (I guess)
1520 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001521 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001522
reed1f836ee2014-07-07 07:49:34 -07001523 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524}
1525
reed@google.com819c9212011-02-23 18:56:55 +00001526#ifdef SK_DEBUG
1527void SkCanvas::validateClip() const {
1528 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001529 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001530 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001531 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001532 return;
1533 }
1534
reed@google.com819c9212011-02-23 18:56:55 +00001535 SkIRect ir;
1536 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001537 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001538
robertphillips@google.com80214e22012-07-20 15:33:18 +00001539 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001540 const SkClipStack::Element* element;
1541 while ((element = iter.next()) != NULL) {
1542 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001543 case SkClipStack::Element::kRect_Type:
1544 element->getRect().round(&ir);
1545 tmpClip.op(ir, element->getOp());
1546 break;
1547 case SkClipStack::Element::kEmpty_Type:
1548 tmpClip.setEmpty();
1549 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001550 default: {
1551 SkPath path;
1552 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001553 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001554 break;
1555 }
reed@google.com819c9212011-02-23 18:56:55 +00001556 }
1557 }
reed@google.com819c9212011-02-23 18:56:55 +00001558}
1559#endif
1560
reed@google.com90c07ea2012-04-13 13:50:27 +00001561void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001562 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001563 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001564
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001565 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001566 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001567 }
1568}
1569
reed@google.com5c3d1472011-02-22 19:12:23 +00001570///////////////////////////////////////////////////////////////////////////////
1571
reed@google.com754de5f2014-02-24 19:38:20 +00001572bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001573 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001574}
1575
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001576bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001577 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001578}
1579
reed@google.com3b3e8952012-08-16 20:53:31 +00001580bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001581 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001582 return true;
1583
reed1f836ee2014-07-07 07:49:34 -07001584 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 return true;
1586 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587
reed1f836ee2014-07-07 07:49:34 -07001588 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001589 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001590 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001591 SkIRect idst;
1592 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001593 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001594 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001595 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001596
reed@android.coma380ae42009-07-21 01:17:02 +00001597 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001598 // TODO: should we use | instead, or compare all 4 at once?
1599 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001600 return true;
1601 }
reed@google.comc0784db2013-12-13 21:16:12 +00001602 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001603 return true;
1604 }
1605 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607}
1608
reed@google.com3b3e8952012-08-16 20:53:31 +00001609bool SkCanvas::quickReject(const SkPath& path) const {
1610 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611}
1612
reed@google.com3b3e8952012-08-16 20:53:31 +00001613bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001614 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001615 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 return false;
1617 }
1618
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001619 SkMatrix inverse;
1620 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001621 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001622 if (bounds) {
1623 bounds->setEmpty();
1624 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001625 return false;
1626 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627
bsalomon49f085d2014-09-05 13:34:00 -07001628 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001629 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001630 // adjust it outwards in case we are antialiasing
1631 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001632
reed@google.com8f4d2302013-12-17 16:44:46 +00001633 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1634 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635 inverse.mapRect(bounds, r);
1636 }
1637 return true;
1638}
1639
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001640bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001641 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001642 if (clip.isEmpty()) {
1643 if (bounds) {
1644 bounds->setEmpty();
1645 }
1646 return false;
1647 }
1648
bsalomon49f085d2014-09-05 13:34:00 -07001649 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001650 *bounds = clip.getBounds();
1651 }
1652 return true;
1653}
1654
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001656 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657}
1658
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001659const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001660 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001661}
1662
reed@google.com9c135db2014-03-12 18:28:35 +00001663GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1664 SkBaseDevice* dev = this->getTopDevice();
1665 return dev ? dev->accessRenderTarget() : NULL;
1666}
1667
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001668SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001669 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001670 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671}
1672
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001673GrContext* SkCanvas::getGrContext() {
1674#if SK_SUPPORT_GPU
1675 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001676 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001677 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001678 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001679 return renderTarget->getContext();
1680 }
1681 }
1682#endif
1683
1684 return NULL;
1685
1686}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001687
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001688void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1689 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001690 TRACE_EVENT0("skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001691 if (outer.isEmpty()) {
1692 return;
1693 }
1694 if (inner.isEmpty()) {
1695 this->drawRRect(outer, paint);
1696 return;
1697 }
1698
1699 // We don't have this method (yet), but technically this is what we should
1700 // be able to assert...
1701 // SkASSERT(outer.contains(inner));
1702 //
1703 // For now at least check for containment of bounds
1704 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1705
1706 this->onDrawDRRect(outer, inner, paint);
1707}
1708
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709//////////////////////////////////////////////////////////////////////////////
1710// These are the virtual drawing methods
1711//////////////////////////////////////////////////////////////////////////////
1712
reed@google.com2a981812011-04-14 18:59:28 +00001713void SkCanvas::clear(SkColor color) {
1714 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001715 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001716 while (iter.next()) {
1717 iter.fDevice->clear(color);
1718 }
1719}
1720
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001721void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001722 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001723 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1724 }
1725}
1726
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727void SkCanvas::drawPaint(const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001728 TRACE_EVENT0("skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001729 this->internalDrawPaint(paint);
1730}
1731
1732void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001733 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734
1735 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001736 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 }
1738
reed@google.com4e2b3d32011-04-07 14:18:59 +00001739 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740}
1741
1742void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1743 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001744 TRACE_EVENT1("skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001745 if ((long)count <= 0) {
1746 return;
1747 }
1748
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001749 SkRect r, storage;
1750 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001751 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001752 // special-case 2 points (common for drawing a single line)
1753 if (2 == count) {
1754 r.set(pts[0], pts[1]);
1755 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001756 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001757 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001758 bounds = &paint.computeFastStrokeBounds(r, &storage);
1759 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001760 return;
1761 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001762 }
reed@google.coma584aed2012-05-16 14:06:02 +00001763
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 SkASSERT(pts != NULL);
1765
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001766 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001767
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001769 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770 }
reed@google.com4b226022011-01-11 18:32:13 +00001771
reed@google.com4e2b3d32011-04-07 14:18:59 +00001772 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773}
1774
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001775void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001776 TRACE_EVENT0("skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001777 SkRect storage;
1778 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001780 bounds = &paint.computeFastBounds(r, &storage);
1781 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782 return;
1783 }
1784 }
reed@google.com4b226022011-01-11 18:32:13 +00001785
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001786 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787
1788 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001789 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790 }
1791
reed@google.com4e2b3d32011-04-07 14:18:59 +00001792 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793}
1794
reed@google.com4ed0fb72012-12-12 20:48:18 +00001795void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001796 TRACE_EVENT0("skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001797 SkRect storage;
1798 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001799 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001800 bounds = &paint.computeFastBounds(oval, &storage);
1801 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001802 return;
1803 }
1804 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001805
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001806 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001807
1808 while (iter.next()) {
1809 iter.fDevice->drawOval(iter, oval, looper.paint());
1810 }
1811
1812 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001813}
1814
1815void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001816 TRACE_EVENT0("skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001817 SkRect storage;
1818 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001819 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001820 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1821 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001822 return;
1823 }
1824 }
1825
1826 if (rrect.isRect()) {
1827 // call the non-virtual version
1828 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001829 return;
1830 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001831 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001832 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1833 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001834 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001835
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001836 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001837
1838 while (iter.next()) {
1839 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1840 }
1841
1842 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001843}
1844
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001845void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1846 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001847 SkRect storage;
1848 const SkRect* bounds = NULL;
1849 if (paint.canComputeFastBounds()) {
1850 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1851 if (this->quickReject(*bounds)) {
1852 return;
1853 }
1854 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001855
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001856 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001857
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001858 while (iter.next()) {
1859 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1860 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001861
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001862 LOOPER_END
1863}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001864
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001865void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001866 TRACE_EVENT0("skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001867 if (!path.isFinite()) {
1868 return;
1869 }
1870
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001871 SkRect storage;
1872 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001873 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001874 const SkRect& pathBounds = path.getBounds();
1875 bounds = &paint.computeFastBounds(pathBounds, &storage);
1876 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001877 return;
1878 }
1879 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001880
1881 const SkRect& r = path.getBounds();
1882 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001883 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001884 this->internalDrawPaint(paint);
1885 }
1886 return;
1887 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001889 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890
1891 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001892 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 }
1894
reed@google.com4e2b3d32011-04-07 14:18:59 +00001895 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896}
1897
piotaixrb5fae932014-09-24 13:03:30 -07001898void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1899 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001900 TRACE_EVENT0("skia", "SkCanvas::drawImage()");
piotaixrb5fae932014-09-24 13:03:30 -07001901 image->draw(this, left, top, paint);
1902}
1903
1904void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1905 const SkRect& dst,
1906 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001907 TRACE_EVENT0("skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001908 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001909}
1910
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1912 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001913 TRACE_EVENT0("skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914 SkDEBUGCODE(bitmap.validate();)
1915
reed@google.com3d608122011-11-21 15:16:16 +00001916 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001917 SkRect bounds = {
1918 x, y,
1919 x + SkIntToScalar(bitmap.width()),
1920 y + SkIntToScalar(bitmap.height())
1921 };
1922 if (paint) {
1923 (void)paint->computeFastBounds(bounds, &bounds);
1924 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001925 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 return;
1927 }
1928 }
reed@google.com4b226022011-01-11 18:32:13 +00001929
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930 SkMatrix matrix;
1931 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001932 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933}
1934
reed@google.com9987ec32011-09-07 11:57:52 +00001935// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001936void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001937 const SkRect& dst, const SkPaint* paint,
1938 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001939 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940 return;
1941 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001942
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001943 SkRect storage;
1944 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001945 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001946 if (paint) {
1947 bounds = &paint->computeFastBounds(dst, &storage);
1948 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001949 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001950 return;
1951 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952 }
reed@google.com3d608122011-11-21 15:16:16 +00001953
reed@google.com33535f32012-09-25 15:37:50 +00001954 SkLazyPaint lazy;
1955 if (NULL == paint) {
1956 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001958
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001959 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001960
reed@google.com33535f32012-09-25 15:37:50 +00001961 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001962 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001963 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001964
reed@google.com33535f32012-09-25 15:37:50 +00001965 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966}
1967
reed@google.com71121732012-09-18 15:14:33 +00001968void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001969 const SkRect& dst, const SkPaint* paint,
1970 DrawBitmapRectFlags flags) {
danakj8f757f52014-11-04 11:48:43 -08001971 TRACE_EVENT0("skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001972 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001973 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001974}
1975
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1977 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001978 TRACE_EVENT0("skia", "SkCanvas::drawBitmapMatrix()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001980 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001981}
1982
reed@google.com9987ec32011-09-07 11:57:52 +00001983void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1984 const SkIRect& center, const SkRect& dst,
1985 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001986 if (bitmap.drawsNothing()) {
1987 return;
1988 }
reed@google.com3d608122011-11-21 15:16:16 +00001989 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001990 SkRect storage;
1991 const SkRect* bounds = &dst;
1992 if (paint) {
1993 bounds = &paint->computeFastBounds(dst, &storage);
1994 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001995 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001996 return;
1997 }
1998 }
1999
reed@google.com9987ec32011-09-07 11:57:52 +00002000 const int32_t w = bitmap.width();
2001 const int32_t h = bitmap.height();
2002
2003 SkIRect c = center;
2004 // pin center to the bounds of the bitmap
2005 c.fLeft = SkMax32(0, center.fLeft);
2006 c.fTop = SkMax32(0, center.fTop);
2007 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2008 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2009
reed@google.com71121732012-09-18 15:14:33 +00002010 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002011 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002012 };
2013 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002014 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002015 };
reed@google.com9987ec32011-09-07 11:57:52 +00002016 SkScalar dstX[4] = {
2017 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2018 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2019 };
2020 SkScalar dstY[4] = {
2021 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2022 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2023 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002024
reed@google.com9987ec32011-09-07 11:57:52 +00002025 if (dstX[1] > dstX[2]) {
2026 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2027 dstX[2] = dstX[1];
2028 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002029
reed@google.com9987ec32011-09-07 11:57:52 +00002030 if (dstY[1] > dstY[2]) {
2031 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2032 dstY[2] = dstY[1];
2033 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002034
reed@google.com9987ec32011-09-07 11:57:52 +00002035 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002036 SkRect s, d;
2037
reed@google.com9987ec32011-09-07 11:57:52 +00002038 s.fTop = srcY[y];
2039 s.fBottom = srcY[y+1];
2040 d.fTop = dstY[y];
2041 d.fBottom = dstY[y+1];
2042 for (int x = 0; x < 3; x++) {
2043 s.fLeft = srcX[x];
2044 s.fRight = srcX[x+1];
2045 d.fLeft = dstX[x];
2046 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002047 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002048 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002049 }
2050 }
2051}
2052
2053void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2054 const SkRect& dst, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002055 TRACE_EVENT0("skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002056 SkDEBUGCODE(bitmap.validate();)
2057
2058 // Need a device entry-point, so gpu can use a mesh
2059 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2060}
2061
reed@google.comf67e4cf2011-03-15 20:56:58 +00002062class SkDeviceFilteredPaint {
2063public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002064 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2065 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002066 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002067 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002068 newPaint->setFlags(flags.fFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002069 fPaint = newPaint;
2070 } else {
2071 fPaint = &paint;
2072 }
2073 }
2074
reed@google.comf67e4cf2011-03-15 20:56:58 +00002075 const SkPaint& paint() const { return *fPaint; }
2076
2077private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002078 const SkPaint* fPaint;
2079 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002080};
2081
bungeman@google.com52c748b2011-08-22 21:30:43 +00002082void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2083 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002084 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002085 draw.fDevice->drawRect(draw, r, paint);
2086 } else {
2087 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002088 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002089 draw.fDevice->drawRect(draw, r, p);
2090 }
2091}
2092
2093void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2094 const char text[], size_t byteLength,
2095 SkScalar x, SkScalar y) {
2096 SkASSERT(byteLength == 0 || text != NULL);
2097
2098 // nothing to draw
2099 if (text == NULL || byteLength == 0 ||
2100 draw.fClip->isEmpty() ||
2101 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2102 return;
2103 }
2104
2105 SkScalar width = 0;
2106 SkPoint start;
2107
2108 start.set(0, 0); // to avoid warning
2109 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2110 SkPaint::kStrikeThruText_Flag)) {
2111 width = paint.measureText(text, byteLength);
2112
2113 SkScalar offsetX = 0;
2114 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2115 offsetX = SkScalarHalf(width);
2116 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2117 offsetX = width;
2118 }
2119 start.set(x - offsetX, y);
2120 }
2121
2122 if (0 == width) {
2123 return;
2124 }
2125
2126 uint32_t flags = paint.getFlags();
2127
2128 if (flags & (SkPaint::kUnderlineText_Flag |
2129 SkPaint::kStrikeThruText_Flag)) {
2130 SkScalar textSize = paint.getTextSize();
2131 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2132 SkRect r;
2133
2134 r.fLeft = start.fX;
2135 r.fRight = start.fX + width;
2136
2137 if (flags & SkPaint::kUnderlineText_Flag) {
2138 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2139 start.fY);
2140 r.fTop = offset;
2141 r.fBottom = offset + height;
2142 DrawRect(draw, paint, r, textSize);
2143 }
2144 if (flags & SkPaint::kStrikeThruText_Flag) {
2145 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2146 start.fY);
2147 r.fTop = offset;
2148 r.fBottom = offset + height;
2149 DrawRect(draw, paint, r, textSize);
2150 }
2151 }
2152}
2153
reed@google.come0d9ce82014-04-23 04:00:17 +00002154void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2155 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002156 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157
2158 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002159 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002160 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002161 DrawTextDecorations(iter, dfp.paint(),
2162 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002163 }
2164
reed@google.com4e2b3d32011-04-07 14:18:59 +00002165 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166}
2167
reed@google.come0d9ce82014-04-23 04:00:17 +00002168void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2169 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002170 SkPoint textOffset = SkPoint::Make(0, 0);
2171
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002172 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002173
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002175 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002176 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002177 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002179
reed@google.com4e2b3d32011-04-07 14:18:59 +00002180 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181}
2182
reed@google.come0d9ce82014-04-23 04:00:17 +00002183void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2184 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002185
2186 SkPoint textOffset = SkPoint::Make(0, constY);
2187
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002188 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002189
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002191 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002192 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002193 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002195
reed@google.com4e2b3d32011-04-07 14:18:59 +00002196 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197}
2198
reed@google.come0d9ce82014-04-23 04:00:17 +00002199void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2200 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002201 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002202
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 while (iter.next()) {
2204 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002205 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002207
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002208 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002209}
2210
fmalita00d5c2c2014-08-21 08:53:26 -07002211void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2212 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002213
fmalita19653d12014-10-16 11:53:30 -07002214 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002215 SkRect storage;
2216
2217 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2218 return;
2219 }
2220 }
2221
fmalitaaa1b9122014-08-28 14:32:24 -07002222 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002223
fmalitaaa1b9122014-08-28 14:32:24 -07002224 while (iter.next()) {
2225 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2226 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002227 }
2228
fmalitaaa1b9122014-08-28 14:32:24 -07002229 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002230}
2231
reed@google.come0d9ce82014-04-23 04:00:17 +00002232// These will become non-virtual, so they always call the (virtual) onDraw... method
2233void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2234 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002235 TRACE_EVENT0("skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002236 this->onDrawText(text, byteLength, x, y, paint);
2237}
2238void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2239 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002240 TRACE_EVENT0("skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002241 this->onDrawPosText(text, byteLength, pos, paint);
2242}
2243void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2244 SkScalar constY, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002245 TRACE_EVENT0("skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002246 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2247}
2248void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2249 const SkMatrix* matrix, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002250 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002251 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2252}
fmalita00d5c2c2014-08-21 08:53:26 -07002253void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2254 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002255 TRACE_EVENT0("skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002256 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002257 this->onDrawTextBlob(blob, x, y, paint);
2258 }
2259}
reed@google.come0d9ce82014-04-23 04:00:17 +00002260
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2262 const SkPoint verts[], const SkPoint texs[],
2263 const SkColor colors[], SkXfermode* xmode,
2264 const uint16_t indices[], int indexCount,
2265 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002266 TRACE_EVENT0("skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002267 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002268
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 while (iter.next()) {
2270 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002271 colors, xmode, indices, indexCount,
2272 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 }
reed@google.com4b226022011-01-11 18:32:13 +00002274
reed@google.com4e2b3d32011-04-07 14:18:59 +00002275 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276}
2277
dandovb3c9d1c2014-08-12 08:34:29 -07002278void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2279 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002280 TRACE_EVENT0("skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002281 if (NULL == cubics) {
2282 return;
2283 }
mtklein6cfa73a2014-08-13 13:33:49 -07002284
dandovecfff212014-08-04 10:02:00 -07002285 // Since a patch is always within the convex hull of the control points, we discard it when its
2286 // bounding rectangle is completely outside the current clip.
2287 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002288 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002289 if (this->quickReject(bounds)) {
2290 return;
2291 }
mtklein6cfa73a2014-08-13 13:33:49 -07002292
dandovb3c9d1c2014-08-12 08:34:29 -07002293 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2294}
2295
2296void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2297 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2298
dandovecfff212014-08-04 10:02:00 -07002299 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002300
dandovecfff212014-08-04 10:02:00 -07002301 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002302 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002303 }
mtklein6cfa73a2014-08-13 13:33:49 -07002304
dandovecfff212014-08-04 10:02:00 -07002305 LOOPER_END
2306}
2307
reed6a070dc2014-11-11 19:36:09 -08002308void SkCanvas::EXPERIMENTAL_drawDrawable(SkCanvasDrawable* dr) {
2309 if (dr) {
2310 SkRect bounds;
2311 if (!dr->getBounds(&bounds) || !this->quickReject(bounds)) {
2312 this->onDrawDrawable(dr);
2313 }
2314 }
2315}
2316
2317void SkCanvas::onDrawDrawable(SkCanvasDrawable* dr) {
2318 dr->draw(this);
2319}
2320
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321//////////////////////////////////////////////////////////////////////////////
2322// These methods are NOT virtual, and therefore must call back into virtual
2323// methods, rather than actually drawing themselves.
2324//////////////////////////////////////////////////////////////////////////////
2325
2326void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002327 SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002328 TRACE_EVENT0("skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329 SkPaint paint;
2330
2331 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002332 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002333 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334 }
2335 this->drawPaint(paint);
2336}
2337
reed@android.com845fdac2009-06-23 03:01:32 +00002338void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002339 TRACE_EVENT0("skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 SkPaint paint;
2341
2342 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002343 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002344 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345 }
2346 this->drawPaint(paint);
2347}
2348
2349void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002350 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002352
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353 pt.set(x, y);
2354 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2355}
2356
2357void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj8f757f52014-11-04 11:48:43 -08002358 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359 SkPoint pt;
2360 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002361
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362 pt.set(x, y);
2363 paint.setColor(color);
2364 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2365}
2366
2367void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2368 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002369 TRACE_EVENT0("skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002371
reed@android.com8a1c16f2008-12-17 15:59:43 +00002372 pts[0].set(x0, y0);
2373 pts[1].set(x1, y1);
2374 this->drawPoints(kLines_PointMode, 2, pts, paint);
2375}
2376
2377void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2378 SkScalar right, SkScalar bottom,
2379 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002380 TRACE_EVENT0("skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002381 SkRect r;
2382
2383 r.set(left, top, right, bottom);
2384 this->drawRect(r, paint);
2385}
2386
2387void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2388 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002389 TRACE_EVENT0("skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 if (radius < 0) {
2391 radius = 0;
2392 }
2393
2394 SkRect r;
2395 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002396 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002397}
2398
2399void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2400 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002401 TRACE_EVENT0("skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402 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) {
danakj8f757f52014-11-04 11:48:43 -08002420 TRACE_EVENT0("skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002421 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2422 this->drawOval(oval, paint);
2423 } else {
2424 SkPath path;
2425 if (useCenter) {
2426 path.moveTo(oval.centerX(), oval.centerY());
2427 }
2428 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2429 if (useCenter) {
2430 path.close();
2431 }
2432 this->drawPath(path, paint);
2433 }
2434}
2435
2436void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2437 const SkPath& path, SkScalar hOffset,
2438 SkScalar vOffset, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002439 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002441
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442 matrix.setTranslate(hOffset, vOffset);
2443 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2444}
2445
reed@android.comf76bacf2009-05-13 14:00:33 +00002446///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002447void SkCanvas::drawPicture(const SkPicture* picture) {
danakj8f757f52014-11-04 11:48:43 -08002448 TRACE_EVENT0("skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002449 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002450 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002451 }
2452}
2453
reedd5fa1a42014-08-09 11:08:05 -07002454void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002455 TRACE_EVENT0("skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002456 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002457 if (matrix && matrix->isIdentity()) {
2458 matrix = NULL;
2459 }
2460 this->onDrawPicture(picture, matrix, paint);
2461 }
2462}
robertphillips9b14f262014-06-04 05:40:44 -07002463
reedd5fa1a42014-08-09 11:08:05 -07002464void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2465 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002466 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002467 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002468 // Canvas has to first give the device the opportunity to render
2469 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002470 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002471 return; // the device has rendered the entire picture
2472 }
2473 }
2474
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002475 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002476
robertphillipsc5ba71d2014-09-04 08:42:50 -07002477 picture->playback(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;
reed84825042014-09-02 12:50:45 -07002553 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002554 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}
reedd5fa1a42014-08-09 11:08:05 -07002575
2576///////////////////////////////////////////////////////////////////////////////
2577
2578SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002579 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002580 : fCanvas(canvas)
2581 , fSaveCount(canvas->getSaveCount())
2582{
bsalomon49f085d2014-09-05 13:34:00 -07002583 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002584 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002585 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002586 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002587 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002588 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002589 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002590 canvas->save();
2591 }
mtklein6cfa73a2014-08-13 13:33:49 -07002592
bsalomon49f085d2014-09-05 13:34:00 -07002593 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002594 canvas->concat(*matrix);
2595 }
2596}
2597
2598SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2599 fCanvas->restoreToCount(fSaveCount);
2600}