blob: afb89b038e59a3bbcb5788abd179712203eb7626 [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
reedd9544982014-09-09 18:46:22 -0700408SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
409 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000410 fCachedLocalClipBounds.setEmpty();
411 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000412 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000413 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700414 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000415 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000416 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000417 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418
419 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700420 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421
reedd9544982014-09-09 18:46:22 -0700422 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424
reed@google.com97af1a62012-08-28 12:19:02 +0000425 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000426
reedf92c8662014-08-18 08:02:43 -0700427 if (device) {
reedb2db8982014-11-13 12:41:02 -0800428 device->initForRootLayer(fProps.pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700429 if (device->forceConservativeRasterClip()) {
430 fConservativeRasterClip = true;
431 }
reedf92c8662014-08-18 08:02:43 -0700432 device->onAttachToCanvas(this);
433 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800434 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700435 }
436 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437}
438
reed@google.comcde92112011-07-06 20:00:52 +0000439SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000440 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700441 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000442{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000443 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000444
reedd9544982014-09-09 18:46:22 -0700445 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000446}
447
reedd9544982014-09-09 18:46:22 -0700448static SkBitmap make_nopixels(int width, int height) {
449 SkBitmap bitmap;
450 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
451 return bitmap;
452}
453
454class SkNoPixelsBitmapDevice : public SkBitmapDevice {
455public:
reed78e27682014-11-19 08:04:34 -0800456 SkNoPixelsBitmapDevice(const SkIRect& bounds)
457 : INHERITED(make_nopixels(bounds.width(), bounds.height()))
458 {
459 this->setOrigin(bounds.x(), bounds.y());
460 }
reedd9544982014-09-09 18:46:22 -0700461
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
reed78e27682014-11-19 08:04:34 -0800473 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
474 (SkIRect::MakeWH(width, height))), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700475}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000476
reed78e27682014-11-19 08:04:34 -0800477SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700478 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700479 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700480{
481 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700482
reed78e27682014-11-19 08:04:34 -0800483 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700484}
485
reed4a8126e2014-09-22 07:29:03 -0700486SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700487 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700488 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700489{
490 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700491
reedd9544982014-09-09 18:46:22 -0700492 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000493}
494
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000495SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000496 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700497 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000498{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700500
reedd9544982014-09-09 18:46:22 -0700501 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502}
503
reed4a8126e2014-09-22 07:29:03 -0700504SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700505 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700506 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700507{
508 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700509
reed4a8126e2014-09-22 07:29:03 -0700510 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
511 this->init(device, kDefault_InitFlags);
512}
reed29c857d2014-09-21 10:25:07 -0700513
reed4a8126e2014-09-22 07:29:03 -0700514SkCanvas::SkCanvas(const SkBitmap& bitmap)
515 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
516 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
517{
518 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700519
reed4a8126e2014-09-22 07:29:03 -0700520 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
521 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
524SkCanvas::~SkCanvas() {
525 // free up the contents of our deque
526 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000527 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 this->internalRestore(); // restore the last, since we're going away
530
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000531 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 dec_canvas();
534}
535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536SkDrawFilter* SkCanvas::getDrawFilter() const {
537 return fMCRec->fFilter;
538}
539
540SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
541 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
542 return filter;
543}
544
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000545SkMetaData& SkCanvas::getMetaData() {
546 // metadata users are rare, so we lazily allocate it. If that changes we
547 // can decide to just make it a field in the device (rather than a ptr)
548 if (NULL == fMetaData) {
549 fMetaData = new SkMetaData;
550 }
551 return *fMetaData;
552}
553
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554///////////////////////////////////////////////////////////////////////////////
555
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000556void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000557 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000558 if (device) {
559 device->flush();
560 }
561}
562
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000563SkISize SkCanvas::getTopLayerSize() const {
564 SkBaseDevice* d = this->getTopDevice();
565 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
566}
567
568SkIPoint SkCanvas::getTopLayerOrigin() const {
569 SkBaseDevice* d = this->getTopDevice();
570 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
571}
572
573SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000574 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000575 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
576}
577
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000578SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000580 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 SkASSERT(rec && rec->fLayer);
582 return rec->fLayer->fDevice;
583}
584
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000585SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000586 if (updateMatrixClip) {
587 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
588 }
reed@google.com9266fed2011-03-30 00:18:03 +0000589 return fMCRec->fTopLayer->fDevice;
590}
591
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000592SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000594 SkDeque::F2BIter iter(fMCStack);
595 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000597 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598
599 if (rootDevice == device) {
600 return device;
601 }
reed@google.com4b226022011-01-11 18:32:13 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000604 device->onAttachToCanvas(this);
reedb2db8982014-11-13 12:41:02 -0800605 device->initForRootLayer(fProps.pixelGeometry());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 }
607 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000608 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 }
610
611 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
612 rootDevice = device;
613
614 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 /* Now we update our initial region to have the bounds of the new device,
617 and then intersect all of the clips in our stack with these bounds,
618 to ensure that we can't draw outside of the device's bounds (and trash
619 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 NOTE: this is only a partial-fix, since if the new device is larger than
622 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000623 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
625 reconstruct the correct clips, so this approximation will have to do.
626 The caller really needs to restore() back to the base if they want to
627 accurately take advantage of the new device bounds.
628 */
629
reed@google.com42aea282012-03-28 16:19:15 +0000630 SkIRect bounds;
631 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000633 } else {
634 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 }
reed@google.com42aea282012-03-28 16:19:15 +0000636 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700637 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000638 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700639 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000640 }
641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 return device;
643}
644
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000645bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
646 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
647 return false;
648 }
649
650 bool weAllocated = false;
651 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700652 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000653 return false;
654 }
655 weAllocated = true;
656 }
657
658 SkBitmap bm(*bitmap);
659 bm.lockPixels();
660 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
661 return true;
662 }
663
664 if (weAllocated) {
665 bitmap->setPixelRef(NULL);
666 }
667 return false;
668}
reed@google.com51df9e32010-12-23 19:29:18 +0000669
bsalomon@google.comc6980972011-11-02 19:57:21 +0000670bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000671 SkIRect r = srcRect;
672 const SkISize size = this->getBaseLayerSize();
673 if (!r.intersect(0, 0, size.width(), size.height())) {
674 bitmap->reset();
675 return false;
676 }
677
reed84825042014-09-02 12:50:45 -0700678 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000679 // bitmap will already be reset.
680 return false;
681 }
682 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
683 bitmap->reset();
684 return false;
685 }
686 return true;
687}
688
689bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
690 switch (origInfo.colorType()) {
691 case kUnknown_SkColorType:
692 case kIndex_8_SkColorType:
693 return false;
694 default:
695 break;
696 }
697 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
698 return false;
699 }
700 if (0 == origInfo.width() || 0 == origInfo.height()) {
701 return false;
702 }
703
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000704 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000705 if (!device) {
706 return false;
707 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000708
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000709 const SkISize size = this->getBaseLayerSize();
710 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
711 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000712 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000713 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000714
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000715 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700716 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000717
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000718 // if x or y are negative, then we have to adjust pixels
719 if (x > 0) {
720 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000721 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000722 if (y > 0) {
723 y = 0;
724 }
725 // here x,y are either 0 or negative
726 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000727
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000728 // The device can assert that the requested area is always contained in its bounds
729 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000730}
731
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000732bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
733 if (bitmap.getTexture()) {
734 return false;
735 }
736 SkBitmap bm(bitmap);
737 bm.lockPixels();
738 if (bm.getPixels()) {
739 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
740 }
741 return false;
742}
743
744bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
745 int x, int y) {
746 switch (origInfo.colorType()) {
747 case kUnknown_SkColorType:
748 case kIndex_8_SkColorType:
749 return false;
750 default:
751 break;
752 }
753 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
754 return false;
755 }
756
757 const SkISize size = this->getBaseLayerSize();
758 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
759 if (!target.intersect(0, 0, size.width(), size.height())) {
760 return false;
761 }
762
763 SkBaseDevice* device = this->getDevice();
764 if (!device) {
765 return false;
766 }
767
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000768 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700769 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000770
771 // if x or y are negative, then we have to adjust pixels
772 if (x > 0) {
773 x = 0;
774 }
775 if (y > 0) {
776 y = 0;
777 }
778 // here x,y are either 0 or negative
779 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
780
reed4af35f32014-06-27 17:47:49 -0700781 // Tell our owning surface to bump its generation ID
782 this->predrawNotify();
783
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000784 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000785 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000786}
reed@google.com51df9e32010-12-23 19:29:18 +0000787
junov@google.com4370aed2012-01-18 16:21:08 +0000788SkCanvas* SkCanvas::canvasForDrawIter() {
789 return this;
790}
791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792//////////////////////////////////////////////////////////////////////////////
793
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794void SkCanvas::updateDeviceCMCache() {
795 if (fDeviceCMDirty) {
796 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700797 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000799
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000801 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000803 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000805 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 } while ((layer = layer->fNext) != NULL);
807 }
808 fDeviceCMDirty = false;
809 }
810}
811
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812///////////////////////////////////////////////////////////////////////////////
813
reedf0090cb2014-11-26 08:55:51 -0800814int SkCanvas::getSaveCount() const {
815 return fMCStack.count();
816}
817
818int SkCanvas::save() {
819 this->willSave();
820 return this->internalSave();
821}
822
823void SkCanvas::restore() {
824 // check for underflow
825 if (fMCStack.count() > 1) {
826 this->willRestore();
827 this->internalRestore();
828 this->didRestore();
829 }
830}
831
832void SkCanvas::restoreToCount(int count) {
833 // sanity check
834 if (count < 1) {
835 count = 1;
836 }
837
838 int n = this->getSaveCount() - count;
839 for (int i = 0; i < n; ++i) {
840 this->restore();
841 }
842}
843
Florin Malita5f6102d2014-06-30 10:13:28 -0400844int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000846
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700848 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000850
Florin Malita5f6102d2014-06-30 10:13:28 -0400851 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000852
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 return saveCount;
854}
855
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000857#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000859#else
860 return true;
861#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862}
863
junov@chromium.orga907ac32012-02-24 21:54:07 +0000864bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000865 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000866 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000867 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000868 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000869 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000870 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000871
872 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700873 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000874 // Filters may grow the bounds beyond the device bounds.
875 op = SkRegion::kReplace_Op;
876 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000877 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700878 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 this->getTotalMatrix().mapRect(&r, *bounds);
882 r.roundOut(&ir);
883 // early exit if the layer's bounds are clipped out
884 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000885 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700886 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000887 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000888 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 }
890 } else { // no user bounds, so just use the clip
891 ir = clipBounds;
892 }
893
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000894 if (bounds_affects_clip(flags)) {
895 fClipStack.clipDevRect(ir, op);
896 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700897 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000898 return false;
899 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000900 }
901
902 if (intersection) {
903 *intersection = ir;
904 }
905 return true;
906}
907
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000908int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
909 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
910 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
911}
912
junov@chromium.orga907ac32012-02-24 21:54:07 +0000913int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
914 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000915 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
916 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000917}
918
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000919int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
920 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000921#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000922 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000923#endif
924
junov@chromium.orga907ac32012-02-24 21:54:07 +0000925 // do this before we create the layer. We don't call the public save() since
926 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400927 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000928
929 fDeviceCMDirty = true;
930
931 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000932 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 return count;
934 }
935
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000936 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
937 // the clipRectBounds() call above?
938 if (kNoLayer_SaveLayerStrategy == strategy) {
939 return count;
940 }
941
reed@google.comb55deeb2012-01-06 14:43:09 +0000942 // Kill the imagefilter if our device doesn't allow it
943 SkLazyPaint lazyP;
944 if (paint && paint->getImageFilter()) {
945 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000946 if (justForImageFilter) {
947 // early exit if the layer was just for the imageFilter
948 return count;
949 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000950 SkPaint* p = lazyP.set(*paint);
951 p->setImageFilter(NULL);
952 paint = p;
953 }
954 }
955
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000956 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
957 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
958 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959
reedb2db8982014-11-13 12:41:02 -0800960 SkBaseDevice* device = this->getTopDevice();
961 if (NULL == device) {
962 SkDebugf("Unable to find device for layer.");
963 return count;
reed@google.com76dd2772012-01-05 21:15:07 +0000964 }
reedb2db8982014-11-13 12:41:02 -0800965
966 SkBaseDevice::Usage usage = SkBaseDevice::kSaveLayer_Usage;
967 if (paint && paint->getImageFilter()) {
968 usage = SkBaseDevice::kImageFilter_Usage;
969 }
970 device = device->onCreateCompatibleDevice(SkBaseDevice::CreateInfo(info, usage,
971 fProps.pixelGeometry()));
bungeman@google.come25c6842011-08-17 14:53:54 +0000972 if (NULL == device) {
973 SkDebugf("Unable to create device for layer.");
974 return count;
975 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000976
reed@google.com6f8f2922011-03-04 22:27:10 +0000977 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700978 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
979 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 device->unref();
981
982 layer->fNext = fMCRec->fTopLayer;
983 fMCRec->fLayer = layer;
984 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
985
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000986 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 return count;
988}
989
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000990int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
991 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
992}
993
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
995 SaveFlags flags) {
996 if (0xFF == alpha) {
997 return this->saveLayer(bounds, NULL, flags);
998 } else {
999 SkPaint tmpPaint;
1000 tmpPaint.setAlpha(alpha);
1001 return this->saveLayer(bounds, &tmpPaint, flags);
1002 }
1003}
1004
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005void SkCanvas::internalRestore() {
1006 SkASSERT(fMCStack.count() != 0);
1007
1008 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001009 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010
Florin Malita5f6102d2014-06-30 10:13:28 -04001011 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001012
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001013 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 DeviceCM* layer = fMCRec->fLayer; // may be null
1015 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1016 fMCRec->fLayer = NULL;
1017
1018 // now do the normal restore()
1019 fMCRec->~MCRec(); // balanced in save()
1020 fMCStack.pop_back();
1021 fMCRec = (MCRec*)fMCStack.back();
1022
1023 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1024 since if we're being recorded, we don't want to record this (the
1025 recorder will have already recorded the restore).
1026 */
bsalomon49f085d2014-09-05 13:34:00 -07001027 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001029 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001030 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1031 layer->fPaint);
1032 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001034
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001035 SkASSERT(fSaveLayerCount > 0);
1036 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 }
1038 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001039 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040}
1041
reed@google.com7c202932011-12-14 18:48:05 +00001042bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001043 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001044}
1045
reed4a8126e2014-09-22 07:29:03 -07001046SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1047 if (NULL == props) {
1048 props = &fProps;
1049 }
1050 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001051}
1052
reed4a8126e2014-09-22 07:29:03 -07001053SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001054 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001055 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001056}
1057
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001058SkImageInfo SkCanvas::imageInfo() const {
1059 SkBaseDevice* dev = this->getDevice();
1060 if (dev) {
1061 return dev->imageInfo();
1062 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001063 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001064 }
1065}
1066
1067const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1068 return this->onPeekPixels(info, rowBytes);
1069}
1070
1071const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1072 SkBaseDevice* dev = this->getDevice();
1073 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1074}
1075
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001076void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1077 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1078 if (pixels && origin) {
1079 *origin = this->getTopDevice(false)->getOrigin();
1080 }
1081 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001082}
1083
1084void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1085 SkBaseDevice* dev = this->getTopDevice();
1086 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1087}
1088
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001089SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1090 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1091 if (NULL == fAddr) {
1092 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001093 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001094 return; // failure, fAddr is NULL
1095 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001096 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1097 return; // failure, fAddr is NULL
1098 }
1099 fAddr = fBitmap.getPixels();
1100 fRowBytes = fBitmap.rowBytes();
1101 }
1102 SkASSERT(fAddr); // success
1103}
1104
1105bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1106 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001107 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001108 } else {
1109 bitmap->reset();
1110 return false;
1111 }
1112}
1113
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001114void SkCanvas::onPushCull(const SkRect& cullRect) {
1115 // do nothing. Subclasses may do something
1116}
1117
1118void SkCanvas::onPopCull() {
1119 // do nothing. Subclasses may do something
1120}
1121
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001123#ifdef SK_DEBUG
1124// Ensure that cull rects are monotonically nested in device space.
1125void SkCanvas::validateCull(const SkIRect& devCull) {
1126 if (fCullStack.isEmpty()
1127 || devCull.isEmpty()
1128 || fCullStack.top().contains(devCull)) {
1129 return;
1130 }
1131
1132 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1133 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1134 fCullStack.top().x(), fCullStack.top().y(),
1135 fCullStack.top().right(), fCullStack.top().bottom()));
1136
1137#ifdef ASSERT_NESTED_CULLING
1138 SkDEBUGFAIL("Invalid cull.");
1139#endif
1140}
1141#endif
1142
1143void SkCanvas::pushCull(const SkRect& cullRect) {
1144 ++fCullCount;
1145 this->onPushCull(cullRect);
1146
1147#ifdef SK_DEBUG
1148 // Map the cull rect into device space.
1149 SkRect mappedCull;
1150 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1151
1152 // Take clipping into account.
1153 SkIRect devClip, devCull;
1154 mappedCull.roundOut(&devCull);
1155 this->getClipDeviceBounds(&devClip);
1156 if (!devCull.intersect(devClip)) {
1157 devCull.setEmpty();
1158 }
1159
1160 this->validateCull(devCull);
1161 fCullStack.push(devCull); // balanced in popCull
1162#endif
1163}
1164
1165void SkCanvas::popCull() {
1166 SkASSERT(fCullStack.count() == fCullCount);
1167
1168 if (fCullCount > 0) {
1169 --fCullCount;
1170 this->onPopCull();
1171
1172 SkDEBUGCODE(fCullStack.pop());
1173 }
1174}
1175
1176/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001178void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001180 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 return;
1182 }
1183
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001184 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001186 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001188
1189 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001190
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001191 SkRect storage;
1192 const SkRect* bounds = NULL;
1193 if (paint && paint->canComputeFastBounds()) {
1194 bitmap.getBounds(&storage);
1195 matrix.mapRect(&storage);
1196 bounds = &paint->computeFastBounds(storage, &storage);
1197 }
1198
1199 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001200
1201 while (iter.next()) {
1202 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1203 }
1204
1205 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206}
1207
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001208void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001209 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 SkPaint tmp;
1211 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 paint = &tmp;
1213 }
reed@google.com4b226022011-01-11 18:32:13 +00001214
reed@google.com8926b162012-03-23 15:36:36 +00001215 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001217 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001218 paint = &looper.paint();
1219 SkImageFilter* filter = paint->getImageFilter();
1220 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001221 if (filter && !dstDev->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001222 SkDeviceImageFilterProxy proxy(dstDev, fProps);
reed@google.com76dd2772012-01-05 21:15:07 +00001223 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001224 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001225 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001226 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001227 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001228 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001229 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001230 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001231 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001232 SkPaint tmpUnfiltered(*paint);
1233 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001234 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1235 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001236 }
1237 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001238 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001239 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001241 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242}
1243
reed@google.com8926b162012-03-23 15:36:36 +00001244void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1245 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001246 TRACE_EVENT0("skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001247 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001248 return;
1249 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001250 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001251
reed@google.com8926b162012-03-23 15:36:36 +00001252 SkPaint tmp;
1253 if (NULL == paint) {
1254 paint = &tmp;
1255 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001256
reed@google.com8926b162012-03-23 15:36:36 +00001257 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001258
reed@google.com8926b162012-03-23 15:36:36 +00001259 while (iter.next()) {
1260 paint = &looper.paint();
1261 SkImageFilter* filter = paint->getImageFilter();
1262 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1263 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001264 SkDeviceImageFilterProxy proxy(iter.fDevice, fProps);
reed@google.com8926b162012-03-23 15:36:36 +00001265 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001266 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001267 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001268 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001269 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001270 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001271 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001272 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001273 SkPaint tmpUnfiltered(*paint);
1274 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001275 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001276 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001277 }
1278 } else {
1279 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1280 }
1281 }
1282 LOOPER_END
1283}
1284
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001286void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001287 SkMatrix m;
1288 m.setTranslate(dx, dy);
1289 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290}
1291
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001292void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001293 SkMatrix m;
1294 m.setScale(sx, sy);
1295 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296}
1297
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001298void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001299 SkMatrix m;
1300 m.setRotate(degrees);
1301 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302}
1303
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001304void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001305 SkMatrix m;
1306 m.setSkew(sx, sy);
1307 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001308}
1309
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001310void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001311 if (matrix.isIdentity()) {
1312 return;
1313 }
1314
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001316 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001317 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001318
1319 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001320}
1321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322void SkCanvas::setMatrix(const SkMatrix& matrix) {
1323 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001324 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001325 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001326 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329void SkCanvas::resetMatrix() {
1330 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001331
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 matrix.reset();
1333 this->setMatrix(matrix);
1334}
1335
1336//////////////////////////////////////////////////////////////////////////////
1337
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001338void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001339 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1340 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001341}
1342
1343void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001344#ifdef SK_ENABLE_CLIP_QUICKREJECT
1345 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001346 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001347 return false;
1348 }
1349
reed@google.com3b3e8952012-08-16 20:53:31 +00001350 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001351 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001352 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001353
1354 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001355 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001356 }
1357 }
1358#endif
1359
reed@google.com5c3d1472011-02-22 19:12:23 +00001360 AutoValidateClip avc(this);
1361
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001363 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001364 if (!fAllowSoftClip) {
1365 edgeStyle = kHard_ClipEdgeStyle;
1366 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367
reed1f836ee2014-07-07 07:49:34 -07001368 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001369 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001370 // the matrix. This means we don't have to a) make a path, and b) tell
1371 // the region code to scan-convert the path, only to discover that it
1372 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374
reed1f836ee2014-07-07 07:49:34 -07001375 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001376 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001377 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001379 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001380 // and clip against that, since it can handle any matrix. However, to
1381 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1382 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 SkPath path;
1384
1385 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001386 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 }
1388}
1389
reed73e714e2014-09-04 09:02:23 -07001390static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1391 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001392 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001393}
1394
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001395void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001396 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001397 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1399 } else {
1400 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001401 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001402}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001403
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001404void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001405 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001406 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001407 AutoValidateClip avc(this);
1408
1409 fDeviceCMDirty = true;
1410 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001411 if (!fAllowSoftClip) {
1412 edgeStyle = kHard_ClipEdgeStyle;
1413 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001414
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001415 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001416
1417 SkPath devPath;
1418 devPath.addRRect(transformedRRect);
1419
reed73e714e2014-09-04 09:02:23 -07001420 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001422 }
1423
1424 SkPath path;
1425 path.addRRect(rrect);
1426 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001427 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001428}
1429
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001430void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001431 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1432 SkRect r;
1433 if (!path.isInverseFillType() && path.isRect(&r)) {
1434 this->onClipRect(r, op, edgeStyle);
1435 } else {
1436 this->onClipPath(path, op, edgeStyle);
1437 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001438}
1439
1440void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001441#ifdef SK_ENABLE_CLIP_QUICKREJECT
1442 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001443 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001444 return false;
1445 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001446
reed@google.com3b3e8952012-08-16 20:53:31 +00001447 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001448 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001449 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001450
reed@google.comda17f752012-08-16 18:27:05 +00001451 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001452 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001453 }
1454 }
1455#endif
1456
reed@google.com5c3d1472011-02-22 19:12:23 +00001457 AutoValidateClip avc(this);
1458
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001460 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001461 if (!fAllowSoftClip) {
1462 edgeStyle = kHard_ClipEdgeStyle;
1463 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464
1465 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001466 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467
reed@google.comfe701122011-11-08 19:41:23 +00001468 // Check if the transfomation, or the original path itself
1469 // made us empty. Note this can also happen if we contained NaN
1470 // values. computing the bounds detects this, and will set our
1471 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1472 if (devPath.getBounds().isEmpty()) {
1473 // resetting the path will remove any NaN or other wanky values
1474 // that might upset our scan converter.
1475 devPath.reset();
1476 }
1477
reed@google.com5c3d1472011-02-22 19:12:23 +00001478 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001479 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001480
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001481 if (fAllowSimplifyClip) {
1482 devPath.reset();
1483 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1484 const SkClipStack* clipStack = getClipStack();
1485 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1486 const SkClipStack::Element* element;
1487 while ((element = iter.next())) {
1488 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001489 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001490 if (type != SkClipStack::Element::kEmpty_Type) {
1491 element->asPath(&operand);
1492 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001493 SkRegion::Op elementOp = element->getOp();
1494 if (elementOp == SkRegion::kReplace_Op) {
1495 devPath = operand;
1496 } else {
1497 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1498 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001499 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1500 // perhaps we need an API change to avoid this sort of mixed-signals about
1501 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001502 if (element->isAA()) {
1503 edgeStyle = kSoft_ClipEdgeStyle;
1504 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001505 }
1506 op = SkRegion::kReplace_Op;
1507 }
1508
reed73e714e2014-09-04 09:02:23 -07001509 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510}
1511
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001512void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001513 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001514}
1515
1516void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001517 AutoValidateClip avc(this);
1518
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001520 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521
reed@google.com5c3d1472011-02-22 19:12:23 +00001522 // todo: signal fClipStack that we have a region, and therefore (I guess)
1523 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001524 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001525
reed1f836ee2014-07-07 07:49:34 -07001526 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527}
1528
reed@google.com819c9212011-02-23 18:56:55 +00001529#ifdef SK_DEBUG
1530void SkCanvas::validateClip() const {
1531 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001532 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001533 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001534 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001535 return;
1536 }
1537
reed@google.com819c9212011-02-23 18:56:55 +00001538 SkIRect ir;
1539 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001540 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001541
robertphillips@google.com80214e22012-07-20 15:33:18 +00001542 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001543 const SkClipStack::Element* element;
1544 while ((element = iter.next()) != NULL) {
1545 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001546 case SkClipStack::Element::kRect_Type:
1547 element->getRect().round(&ir);
1548 tmpClip.op(ir, element->getOp());
1549 break;
1550 case SkClipStack::Element::kEmpty_Type:
1551 tmpClip.setEmpty();
1552 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001553 default: {
1554 SkPath path;
1555 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001556 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001557 break;
1558 }
reed@google.com819c9212011-02-23 18:56:55 +00001559 }
1560 }
reed@google.com819c9212011-02-23 18:56:55 +00001561}
1562#endif
1563
reed@google.com90c07ea2012-04-13 13:50:27 +00001564void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001565 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001566 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001567
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001568 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001569 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001570 }
1571}
1572
reed@google.com5c3d1472011-02-22 19:12:23 +00001573///////////////////////////////////////////////////////////////////////////////
1574
reed@google.com754de5f2014-02-24 19:38:20 +00001575bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001576 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001577}
1578
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001579bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001580 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001581}
1582
reed@google.com3b3e8952012-08-16 20:53:31 +00001583bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001584 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001585 return true;
1586
reed1f836ee2014-07-07 07:49:34 -07001587 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 return true;
1589 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590
reed1f836ee2014-07-07 07:49:34 -07001591 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001592 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001593 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001594 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001595 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001596 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001597
reed@android.coma380ae42009-07-21 01:17:02 +00001598 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001599 // TODO: should we use | instead, or compare all 4 at once?
1600 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001601 return true;
1602 }
reed@google.comc0784db2013-12-13 21:16:12 +00001603 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001604 return true;
1605 }
1606 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
reed@google.com3b3e8952012-08-16 20:53:31 +00001610bool SkCanvas::quickReject(const SkPath& path) const {
1611 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612}
1613
reed@google.com3b3e8952012-08-16 20:53:31 +00001614bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001615 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 return false;
1618 }
1619
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001620 SkMatrix inverse;
1621 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001622 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001623 if (bounds) {
1624 bounds->setEmpty();
1625 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001626 return false;
1627 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628
bsalomon49f085d2014-09-05 13:34:00 -07001629 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001630 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001631 // adjust it outwards in case we are antialiasing
1632 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001633
reed@google.com8f4d2302013-12-17 16:44:46 +00001634 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1635 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636 inverse.mapRect(bounds, r);
1637 }
1638 return true;
1639}
1640
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001641bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001642 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001643 if (clip.isEmpty()) {
1644 if (bounds) {
1645 bounds->setEmpty();
1646 }
1647 return false;
1648 }
1649
bsalomon49f085d2014-09-05 13:34:00 -07001650 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001651 *bounds = clip.getBounds();
1652 }
1653 return true;
1654}
1655
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001657 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658}
1659
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001660const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001661 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001662}
1663
reed@google.com9c135db2014-03-12 18:28:35 +00001664GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1665 SkBaseDevice* dev = this->getTopDevice();
1666 return dev ? dev->accessRenderTarget() : NULL;
1667}
1668
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001669GrContext* SkCanvas::getGrContext() {
1670#if SK_SUPPORT_GPU
1671 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001672 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001673 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001674 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001675 return renderTarget->getContext();
1676 }
1677 }
1678#endif
1679
1680 return NULL;
1681
1682}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001683
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001684void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1685 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001686 TRACE_EVENT0("skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001687 if (outer.isEmpty()) {
1688 return;
1689 }
1690 if (inner.isEmpty()) {
1691 this->drawRRect(outer, paint);
1692 return;
1693 }
1694
1695 // We don't have this method (yet), but technically this is what we should
1696 // be able to assert...
1697 // SkASSERT(outer.contains(inner));
1698 //
1699 // For now at least check for containment of bounds
1700 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1701
1702 this->onDrawDRRect(outer, inner, paint);
1703}
1704
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705//////////////////////////////////////////////////////////////////////////////
1706// These are the virtual drawing methods
1707//////////////////////////////////////////////////////////////////////////////
1708
reed@google.com2a981812011-04-14 18:59:28 +00001709void SkCanvas::clear(SkColor color) {
fmalita8ebaa712014-11-20 20:44:04 -08001710 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001711 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001712 while (iter.next()) {
1713 iter.fDevice->clear(color);
1714 }
1715}
1716
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001717void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001718 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001719 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1720 }
1721}
1722
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723void SkCanvas::drawPaint(const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001724 TRACE_EVENT0("skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001725 this->internalDrawPaint(paint);
1726}
1727
1728void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001729 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730
1731 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001732 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733 }
1734
reed@google.com4e2b3d32011-04-07 14:18:59 +00001735 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736}
1737
1738void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1739 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001740 TRACE_EVENT1("skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741 if ((long)count <= 0) {
1742 return;
1743 }
1744
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001745 SkRect r, storage;
1746 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001747 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001748 // special-case 2 points (common for drawing a single line)
1749 if (2 == count) {
1750 r.set(pts[0], pts[1]);
1751 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001752 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001753 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001754 bounds = &paint.computeFastStrokeBounds(r, &storage);
1755 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001756 return;
1757 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001758 }
reed@google.coma584aed2012-05-16 14:06:02 +00001759
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760 SkASSERT(pts != NULL);
1761
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001762 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001763
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001765 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766 }
reed@google.com4b226022011-01-11 18:32:13 +00001767
reed@google.com4e2b3d32011-04-07 14:18:59 +00001768 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769}
1770
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001771void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001772 TRACE_EVENT0("skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001773 SkRect storage;
1774 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001776 bounds = &paint.computeFastBounds(r, &storage);
1777 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778 return;
1779 }
1780 }
reed@google.com4b226022011-01-11 18:32:13 +00001781
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001782 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783
1784 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001785 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 }
1787
reed@google.com4e2b3d32011-04-07 14:18:59 +00001788 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789}
1790
reed@google.com4ed0fb72012-12-12 20:48:18 +00001791void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001792 TRACE_EVENT0("skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001793 SkRect storage;
1794 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001795 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 bounds = &paint.computeFastBounds(oval, &storage);
1797 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001798 return;
1799 }
1800 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001801
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001802 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001803
1804 while (iter.next()) {
1805 iter.fDevice->drawOval(iter, oval, looper.paint());
1806 }
1807
1808 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001809}
1810
1811void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001812 TRACE_EVENT0("skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001813 SkRect storage;
1814 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001815 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001816 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1817 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001818 return;
1819 }
1820 }
1821
1822 if (rrect.isRect()) {
1823 // call the non-virtual version
1824 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001825 return;
1826 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001827 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001828 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1829 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001830 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001831
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001832 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001833
1834 while (iter.next()) {
1835 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1836 }
1837
1838 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001839}
1840
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001841void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1842 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001843 SkRect storage;
1844 const SkRect* bounds = NULL;
1845 if (paint.canComputeFastBounds()) {
1846 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1847 if (this->quickReject(*bounds)) {
1848 return;
1849 }
1850 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001851
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001852 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001853
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001854 while (iter.next()) {
1855 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1856 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001857
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001858 LOOPER_END
1859}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001860
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001861void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001862 TRACE_EVENT0("skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001863 if (!path.isFinite()) {
1864 return;
1865 }
1866
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001867 SkRect storage;
1868 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001869 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001870 const SkRect& pathBounds = path.getBounds();
1871 bounds = &paint.computeFastBounds(pathBounds, &storage);
1872 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001873 return;
1874 }
1875 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001876
1877 const SkRect& r = path.getBounds();
1878 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001879 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001880 this->internalDrawPaint(paint);
1881 }
1882 return;
1883 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001884
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001885 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886
1887 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001888 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889 }
1890
reed@google.com4e2b3d32011-04-07 14:18:59 +00001891 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892}
1893
piotaixrb5fae932014-09-24 13:03:30 -07001894void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1895 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001896 TRACE_EVENT0("skia", "SkCanvas::drawImage()");
piotaixrb5fae932014-09-24 13:03:30 -07001897 image->draw(this, left, top, paint);
1898}
1899
1900void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1901 const SkRect& dst,
1902 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001903 TRACE_EVENT0("skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001904 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001905}
1906
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1908 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001909 TRACE_EVENT0("skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910 SkDEBUGCODE(bitmap.validate();)
1911
reed@google.com3d608122011-11-21 15:16:16 +00001912 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001913 SkRect bounds = {
1914 x, y,
1915 x + SkIntToScalar(bitmap.width()),
1916 y + SkIntToScalar(bitmap.height())
1917 };
1918 if (paint) {
1919 (void)paint->computeFastBounds(bounds, &bounds);
1920 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001921 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001922 return;
1923 }
1924 }
reed@google.com4b226022011-01-11 18:32:13 +00001925
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 SkMatrix matrix;
1927 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001928 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929}
1930
reed@google.com9987ec32011-09-07 11:57:52 +00001931// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001932void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001933 const SkRect& dst, const SkPaint* paint,
1934 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001935 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936 return;
1937 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001938
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001939 SkRect storage;
1940 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001941 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001942 if (paint) {
1943 bounds = &paint->computeFastBounds(dst, &storage);
1944 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001945 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001946 return;
1947 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948 }
reed@google.com3d608122011-11-21 15:16:16 +00001949
reed@google.com33535f32012-09-25 15:37:50 +00001950 SkLazyPaint lazy;
1951 if (NULL == paint) {
1952 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001954
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001955 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001956
reed@google.com33535f32012-09-25 15:37:50 +00001957 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001958 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001959 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001960
reed@google.com33535f32012-09-25 15:37:50 +00001961 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962}
1963
reed@google.com71121732012-09-18 15:14:33 +00001964void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001965 const SkRect& dst, const SkPaint* paint,
1966 DrawBitmapRectFlags flags) {
danakj8f757f52014-11-04 11:48:43 -08001967 TRACE_EVENT0("skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001968 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001969 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001970}
1971
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1973 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001974 TRACE_EVENT0("skia", "SkCanvas::drawBitmapMatrix()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001976 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977}
1978
reed@google.com9987ec32011-09-07 11:57:52 +00001979void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1980 const SkIRect& center, const SkRect& dst,
1981 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001982 if (bitmap.drawsNothing()) {
1983 return;
1984 }
reed@google.com3d608122011-11-21 15:16:16 +00001985 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001986 SkRect storage;
1987 const SkRect* bounds = &dst;
1988 if (paint) {
1989 bounds = &paint->computeFastBounds(dst, &storage);
1990 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001991 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001992 return;
1993 }
1994 }
1995
reed@google.com9987ec32011-09-07 11:57:52 +00001996 const int32_t w = bitmap.width();
1997 const int32_t h = bitmap.height();
1998
1999 SkIRect c = center;
2000 // pin center to the bounds of the bitmap
2001 c.fLeft = SkMax32(0, center.fLeft);
2002 c.fTop = SkMax32(0, center.fTop);
2003 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2004 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2005
reed@google.com71121732012-09-18 15:14:33 +00002006 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002007 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002008 };
2009 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002010 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002011 };
reed@google.com9987ec32011-09-07 11:57:52 +00002012 SkScalar dstX[4] = {
2013 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2014 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2015 };
2016 SkScalar dstY[4] = {
2017 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2018 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2019 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002020
reed@google.com9987ec32011-09-07 11:57:52 +00002021 if (dstX[1] > dstX[2]) {
2022 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2023 dstX[2] = dstX[1];
2024 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002025
reed@google.com9987ec32011-09-07 11:57:52 +00002026 if (dstY[1] > dstY[2]) {
2027 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2028 dstY[2] = dstY[1];
2029 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002030
reed@google.com9987ec32011-09-07 11:57:52 +00002031 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002032 SkRect s, d;
2033
reed@google.com9987ec32011-09-07 11:57:52 +00002034 s.fTop = srcY[y];
2035 s.fBottom = srcY[y+1];
2036 d.fTop = dstY[y];
2037 d.fBottom = dstY[y+1];
2038 for (int x = 0; x < 3; x++) {
2039 s.fLeft = srcX[x];
2040 s.fRight = srcX[x+1];
2041 d.fLeft = dstX[x];
2042 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002043 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002044 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002045 }
2046 }
2047}
2048
2049void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2050 const SkRect& dst, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002051 TRACE_EVENT0("skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002052 SkDEBUGCODE(bitmap.validate();)
2053
2054 // Need a device entry-point, so gpu can use a mesh
2055 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2056}
2057
reed@google.comf67e4cf2011-03-15 20:56:58 +00002058class SkDeviceFilteredPaint {
2059public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002060 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002061 uint32_t filteredFlags = device->filterTextFlags(paint);
2062 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002063 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002064 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002065 fPaint = newPaint;
2066 } else {
2067 fPaint = &paint;
2068 }
2069 }
2070
reed@google.comf67e4cf2011-03-15 20:56:58 +00002071 const SkPaint& paint() const { return *fPaint; }
2072
2073private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002074 const SkPaint* fPaint;
2075 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002076};
2077
bungeman@google.com52c748b2011-08-22 21:30:43 +00002078void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2079 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002080 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002081 draw.fDevice->drawRect(draw, r, paint);
2082 } else {
2083 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002084 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002085 draw.fDevice->drawRect(draw, r, p);
2086 }
2087}
2088
2089void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2090 const char text[], size_t byteLength,
2091 SkScalar x, SkScalar y) {
2092 SkASSERT(byteLength == 0 || text != NULL);
2093
2094 // nothing to draw
2095 if (text == NULL || byteLength == 0 ||
2096 draw.fClip->isEmpty() ||
2097 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2098 return;
2099 }
2100
2101 SkScalar width = 0;
2102 SkPoint start;
2103
2104 start.set(0, 0); // to avoid warning
2105 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2106 SkPaint::kStrikeThruText_Flag)) {
2107 width = paint.measureText(text, byteLength);
2108
2109 SkScalar offsetX = 0;
2110 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2111 offsetX = SkScalarHalf(width);
2112 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2113 offsetX = width;
2114 }
2115 start.set(x - offsetX, y);
2116 }
2117
2118 if (0 == width) {
2119 return;
2120 }
2121
2122 uint32_t flags = paint.getFlags();
2123
2124 if (flags & (SkPaint::kUnderlineText_Flag |
2125 SkPaint::kStrikeThruText_Flag)) {
2126 SkScalar textSize = paint.getTextSize();
2127 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2128 SkRect r;
2129
2130 r.fLeft = start.fX;
2131 r.fRight = start.fX + width;
2132
2133 if (flags & SkPaint::kUnderlineText_Flag) {
2134 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2135 start.fY);
2136 r.fTop = offset;
2137 r.fBottom = offset + height;
2138 DrawRect(draw, paint, r, textSize);
2139 }
2140 if (flags & SkPaint::kStrikeThruText_Flag) {
2141 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2142 start.fY);
2143 r.fTop = offset;
2144 r.fBottom = offset + height;
2145 DrawRect(draw, paint, r, textSize);
2146 }
2147 }
2148}
2149
reed@google.come0d9ce82014-04-23 04:00:17 +00002150void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2151 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002152 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153
2154 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002155 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002156 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002157 DrawTextDecorations(iter, dfp.paint(),
2158 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 }
2160
reed@google.com4e2b3d32011-04-07 14:18:59 +00002161 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162}
2163
reed@google.come0d9ce82014-04-23 04:00:17 +00002164void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2165 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002166 SkPoint textOffset = SkPoint::Make(0, 0);
2167
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002168 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002169
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002171 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002172 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002173 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002175
reed@google.com4e2b3d32011-04-07 14:18:59 +00002176 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177}
2178
reed@google.come0d9ce82014-04-23 04:00:17 +00002179void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2180 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002181
2182 SkPoint textOffset = SkPoint::Make(0, constY);
2183
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002184 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002185
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002187 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002188 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002189 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002191
reed@google.com4e2b3d32011-04-07 14:18:59 +00002192 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193}
2194
reed@google.come0d9ce82014-04-23 04:00:17 +00002195void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2196 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002197 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002198
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199 while (iter.next()) {
2200 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002201 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002203
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002204 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002205}
2206
fmalita00d5c2c2014-08-21 08:53:26 -07002207void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2208 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002209
fmalita19653d12014-10-16 11:53:30 -07002210 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002211 SkRect storage;
2212
2213 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2214 return;
2215 }
2216 }
2217
fmalitaaa1b9122014-08-28 14:32:24 -07002218 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002219
fmalitaaa1b9122014-08-28 14:32:24 -07002220 while (iter.next()) {
2221 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2222 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002223 }
2224
fmalitaaa1b9122014-08-28 14:32:24 -07002225 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002226}
2227
reed@google.come0d9ce82014-04-23 04:00:17 +00002228// These will become non-virtual, so they always call the (virtual) onDraw... method
2229void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2230 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002231 TRACE_EVENT0("skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002232 this->onDrawText(text, byteLength, x, y, paint);
2233}
2234void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2235 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002236 TRACE_EVENT0("skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002237 this->onDrawPosText(text, byteLength, pos, paint);
2238}
2239void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2240 SkScalar constY, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002241 TRACE_EVENT0("skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002242 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2243}
2244void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2245 const SkMatrix* matrix, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002246 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002247 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2248}
fmalita00d5c2c2014-08-21 08:53:26 -07002249void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2250 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002251 TRACE_EVENT0("skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002252 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002253 this->onDrawTextBlob(blob, x, y, paint);
2254 }
2255}
reed@google.come0d9ce82014-04-23 04:00:17 +00002256
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2258 const SkPoint verts[], const SkPoint texs[],
2259 const SkColor colors[], SkXfermode* xmode,
2260 const uint16_t indices[], int indexCount,
2261 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002262 TRACE_EVENT0("skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002263 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002264
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265 while (iter.next()) {
2266 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002267 colors, xmode, indices, indexCount,
2268 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 }
reed@google.com4b226022011-01-11 18:32:13 +00002270
reed@google.com4e2b3d32011-04-07 14:18:59 +00002271 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272}
2273
dandovb3c9d1c2014-08-12 08:34:29 -07002274void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2275 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002276 TRACE_EVENT0("skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002277 if (NULL == cubics) {
2278 return;
2279 }
mtklein6cfa73a2014-08-13 13:33:49 -07002280
dandovecfff212014-08-04 10:02:00 -07002281 // Since a patch is always within the convex hull of the control points, we discard it when its
2282 // bounding rectangle is completely outside the current clip.
2283 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002284 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002285 if (this->quickReject(bounds)) {
2286 return;
2287 }
mtklein6cfa73a2014-08-13 13:33:49 -07002288
dandovb3c9d1c2014-08-12 08:34:29 -07002289 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2290}
2291
2292void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2293 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2294
dandovecfff212014-08-04 10:02:00 -07002295 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002296
dandovecfff212014-08-04 10:02:00 -07002297 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002298 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002299 }
mtklein6cfa73a2014-08-13 13:33:49 -07002300
dandovecfff212014-08-04 10:02:00 -07002301 LOOPER_END
2302}
2303
reed6a070dc2014-11-11 19:36:09 -08002304void SkCanvas::EXPERIMENTAL_drawDrawable(SkCanvasDrawable* dr) {
reed6be2aa92014-11-18 11:08:05 -08002305 if (dr && !this->quickReject(dr->getBounds())) {
2306 this->onDrawDrawable(dr);
reed6a070dc2014-11-11 19:36:09 -08002307 }
2308}
2309
2310void SkCanvas::onDrawDrawable(SkCanvasDrawable* dr) {
2311 dr->draw(this);
2312}
2313
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314//////////////////////////////////////////////////////////////////////////////
2315// These methods are NOT virtual, and therefore must call back into virtual
2316// methods, rather than actually drawing themselves.
2317//////////////////////////////////////////////////////////////////////////////
2318
2319void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002320 SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002321 TRACE_EVENT0("skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322 SkPaint paint;
2323
2324 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002325 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002326 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002327 }
2328 this->drawPaint(paint);
2329}
2330
reed@android.com845fdac2009-06-23 03:01:32 +00002331void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002332 TRACE_EVENT0("skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 SkPaint paint;
2334
2335 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002336 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002337 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338 }
2339 this->drawPaint(paint);
2340}
2341
2342void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002343 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002345
reed@android.com8a1c16f2008-12-17 15:59:43 +00002346 pt.set(x, y);
2347 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2348}
2349
2350void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj8f757f52014-11-04 11:48:43 -08002351 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 SkPoint pt;
2353 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002354
reed@android.com8a1c16f2008-12-17 15:59:43 +00002355 pt.set(x, y);
2356 paint.setColor(color);
2357 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2358}
2359
2360void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2361 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002362 TRACE_EVENT0("skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002363 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002364
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 pts[0].set(x0, y0);
2366 pts[1].set(x1, y1);
2367 this->drawPoints(kLines_PointMode, 2, pts, paint);
2368}
2369
2370void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2371 SkScalar right, SkScalar bottom,
2372 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002373 TRACE_EVENT0("skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 SkRect r;
2375
2376 r.set(left, top, right, bottom);
2377 this->drawRect(r, paint);
2378}
2379
2380void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2381 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002382 TRACE_EVENT0("skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383 if (radius < 0) {
2384 radius = 0;
2385 }
2386
2387 SkRect r;
2388 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002389 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390}
2391
2392void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2393 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002394 TRACE_EVENT0("skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395 if (rx > 0 && ry > 0) {
2396 if (paint.canComputeFastBounds()) {
2397 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002398 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 return;
2400 }
2401 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002402 SkRRect rrect;
2403 rrect.setRectXY(r, rx, ry);
2404 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 } else {
2406 this->drawRect(r, paint);
2407 }
2408}
2409
reed@android.com8a1c16f2008-12-17 15:59:43 +00002410void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2411 SkScalar sweepAngle, bool useCenter,
2412 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002413 TRACE_EVENT0("skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002414 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2415 this->drawOval(oval, paint);
2416 } else {
2417 SkPath path;
2418 if (useCenter) {
2419 path.moveTo(oval.centerX(), oval.centerY());
2420 }
2421 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2422 if (useCenter) {
2423 path.close();
2424 }
2425 this->drawPath(path, paint);
2426 }
2427}
2428
2429void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2430 const SkPath& path, SkScalar hOffset,
2431 SkScalar vOffset, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002432 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002434
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435 matrix.setTranslate(hOffset, vOffset);
2436 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2437}
2438
reed@android.comf76bacf2009-05-13 14:00:33 +00002439///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002440void SkCanvas::drawPicture(const SkPicture* picture) {
danakj8f757f52014-11-04 11:48:43 -08002441 TRACE_EVENT0("skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002442 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002443 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002444 }
2445}
2446
reedd5fa1a42014-08-09 11:08:05 -07002447void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002448 TRACE_EVENT0("skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002449 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002450 if (matrix && matrix->isIdentity()) {
2451 matrix = NULL;
2452 }
2453 this->onDrawPicture(picture, matrix, paint);
2454 }
2455}
robertphillips9b14f262014-06-04 05:40:44 -07002456
reedd5fa1a42014-08-09 11:08:05 -07002457void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2458 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002459 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002460 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002461 // Canvas has to first give the device the opportunity to render
2462 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002463 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002464 return; // the device has rendered the entire picture
2465 }
2466 }
2467
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002468 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002469
robertphillipsc5ba71d2014-09-04 08:42:50 -07002470 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471}
2472
reed@android.com8a1c16f2008-12-17 15:59:43 +00002473///////////////////////////////////////////////////////////////////////////////
2474///////////////////////////////////////////////////////////////////////////////
2475
2476SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002477 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478
2479 SkASSERT(canvas);
2480
2481 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2482 fDone = !fImpl->next();
2483}
2484
2485SkCanvas::LayerIter::~LayerIter() {
2486 fImpl->~SkDrawIter();
2487}
2488
2489void SkCanvas::LayerIter::next() {
2490 fDone = !fImpl->next();
2491}
2492
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002493SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002494 return fImpl->getDevice();
2495}
2496
2497const SkMatrix& SkCanvas::LayerIter::matrix() const {
2498 return fImpl->getMatrix();
2499}
2500
2501const SkPaint& SkCanvas::LayerIter::paint() const {
2502 const SkPaint* paint = fImpl->getPaint();
2503 if (NULL == paint) {
2504 paint = &fDefaultPaint;
2505 }
2506 return *paint;
2507}
2508
2509const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2510int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2511int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002512
2513///////////////////////////////////////////////////////////////////////////////
2514
fmalitac3b589a2014-06-05 12:40:07 -07002515SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002516
2517///////////////////////////////////////////////////////////////////////////////
2518
2519static bool supported_for_raster_canvas(const SkImageInfo& info) {
2520 switch (info.alphaType()) {
2521 case kPremul_SkAlphaType:
2522 case kOpaque_SkAlphaType:
2523 break;
2524 default:
2525 return false;
2526 }
2527
2528 switch (info.colorType()) {
2529 case kAlpha_8_SkColorType:
2530 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002531 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002532 break;
2533 default:
2534 return false;
2535 }
2536
2537 return true;
2538}
2539
2540SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2541 if (!supported_for_raster_canvas(info)) {
2542 return NULL;
2543 }
2544
2545 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002546 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002547 return NULL;
2548 }
2549
2550 // should this functionality be moved into allocPixels()?
2551 if (!bitmap.info().isOpaque()) {
2552 bitmap.eraseColor(0);
2553 }
2554 return SkNEW_ARGS(SkCanvas, (bitmap));
2555}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002556
2557SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2558 if (!supported_for_raster_canvas(info)) {
2559 return NULL;
2560 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002561
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002562 SkBitmap bitmap;
2563 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2564 return NULL;
2565 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002566 return SkNEW_ARGS(SkCanvas, (bitmap));
2567}
reedd5fa1a42014-08-09 11:08:05 -07002568
2569///////////////////////////////////////////////////////////////////////////////
2570
2571SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002572 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002573 : fCanvas(canvas)
2574 , fSaveCount(canvas->getSaveCount())
2575{
bsalomon49f085d2014-09-05 13:34:00 -07002576 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002577 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002578 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002579 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002580 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002581 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002582 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002583 canvas->save();
2584 }
mtklein6cfa73a2014-08-13 13:33:49 -07002585
bsalomon49f085d2014-09-05 13:34:00 -07002586 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002587 canvas->concat(*matrix);
2588 }
2589}
2590
2591SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2592 fCanvas->restoreToCount(fSaveCount);
2593}