blob: 3880fa9d733f1cd843a67829ca7099b63d585d87 [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
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
bungemand3ebb482015-08-05 13:57:49 -070013#include "SkDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080015#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkDrawFilter.h"
17#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080018#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070019#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080020#include "SkImage_Base.h"
21#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000022#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070023#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070024#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070025#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000027#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080028#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000029#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000030#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000031#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070032#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000033#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000034#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080035#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070036
37#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000039#if SK_SUPPORT_GPU
40#include "GrRenderTarget.h"
41#endif
42
senorblanco87e066e2015-10-28 11:23:36 -070043#define SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
senorblanco87e066e2015-10-28 11:23:36 -070044
reedc83a2972015-07-16 07:40:45 -070045/*
46 * Return true if the drawing this rect would hit every pixels in the canvas.
47 *
48 * Returns false if
49 * - rect does not contain the canvas' bounds
50 * - paint is not fill
51 * - paint would blur or otherwise change the coverage of the rect
52 */
53bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
54 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070055 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
56 (int)kNone_ShaderOverrideOpacity,
57 "need_matching_enums0");
58 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
59 (int)kOpaque_ShaderOverrideOpacity,
60 "need_matching_enums1");
61 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
62 (int)kNotOpaque_ShaderOverrideOpacity,
63 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070064
65 const SkISize size = this->getBaseLayerSize();
66 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
67 if (!this->getClipStack()->quickContains(bounds)) {
68 return false;
69 }
70
71 if (rect) {
72 if (!this->getTotalMatrix().rectStaysRect()) {
73 return false; // conservative
74 }
75
76 SkRect devRect;
77 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070078 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070079 return false;
80 }
81 }
82
83 if (paint) {
84 SkPaint::Style paintStyle = paint->getStyle();
85 if (!(paintStyle == SkPaint::kFill_Style ||
86 paintStyle == SkPaint::kStrokeAndFill_Style)) {
87 return false;
88 }
89 if (paint->getMaskFilter() || paint->getLooper()
90 || paint->getPathEffect() || paint->getImageFilter()) {
91 return false; // conservative
92 }
93 }
94 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
95}
96
97///////////////////////////////////////////////////////////////////////////////////////////////////
98
reedd990e2f2014-12-22 11:58:30 -080099static bool gIgnoreSaveLayerBounds;
100void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
101 gIgnoreSaveLayerBounds = ignore;
102}
103bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
104 return gIgnoreSaveLayerBounds;
105}
106
reed0acf1b42014-12-22 16:12:38 -0800107static bool gTreatSpriteAsBitmap;
108void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
109 gTreatSpriteAsBitmap = spriteAsBitmap;
110}
111bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
112 return gTreatSpriteAsBitmap;
113}
114
reed@google.comda17f752012-08-16 18:27:05 +0000115// experimental for faster tiled drawing...
116//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +0000117
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118//#define SK_TRACE_SAVERESTORE
119
120#ifdef SK_TRACE_SAVERESTORE
121 static int gLayerCounter;
122 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
123 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
124
125 static int gRecCounter;
126 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
127 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
128
129 static int gCanvasCounter;
130 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
131 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
132#else
133 #define inc_layer()
134 #define dec_layer()
135 #define inc_rec()
136 #define dec_rec()
137 #define inc_canvas()
138 #define dec_canvas()
139#endif
140
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000141typedef SkTLazy<SkPaint> SkLazyPaint;
142
reedc83a2972015-07-16 07:40:45 -0700143void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000144 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700145 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
146 ? SkSurface::kDiscard_ContentChangeMode
147 : SkSurface::kRetain_ContentChangeMode);
148 }
149}
150
151void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
152 ShaderOverrideOpacity overrideOpacity) {
153 if (fSurfaceBase) {
154 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
155 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
156 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
157 // and therefore we don't care which mode we're in.
158 //
159 if (fSurfaceBase->outstandingImageSnapshot()) {
160 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
161 mode = SkSurface::kDiscard_ContentChangeMode;
162 }
163 }
164 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000165 }
166}
167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
reed4a8126e2014-09-22 07:29:03 -0700170static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
171 const uint32_t propFlags = props.flags();
172 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
173 flags &= ~SkPaint::kDither_Flag;
174 }
175 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
176 flags &= ~SkPaint::kAntiAlias_Flag;
177 }
178 return flags;
179}
180
181///////////////////////////////////////////////////////////////////////////////
182
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000183/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 The clip/matrix/proc are fields that reflect the top of the save/restore
185 stack. Whenever the canvas changes, it marks a dirty flag, and then before
186 these are used (assuming we're not on a layer) we rebuild these cache
187 values: they reflect the top of the save stack, but translated and clipped
188 by the device's XY offset and bitmap-bounds.
189*/
190struct DeviceCM {
191 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000192 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000193 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000194 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700195 const SkMatrix* fMatrix;
196 SkMatrix fMatrixStorage;
197 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198
reed96e657d2015-03-10 17:30:07 -0700199 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700200 bool conservativeRasterClip, bool deviceIsBitmapDevice)
halcanary96fcdcc2015-08-27 07:41:13 -0700201 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700202 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700203 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700204 {
halcanary96fcdcc2015-08-27 07:41:13 -0700205 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000207 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 }
reed@google.com4b226022011-01-11 18:32:13 +0000209 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700210 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000211 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000213 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700214 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000215 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 fDevice->unref();
217 }
halcanary385fe4d2015-08-26 13:07:48 -0700218 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000219 }
reed@google.com4b226022011-01-11 18:32:13 +0000220
mtkleinfeaadee2015-04-08 11:25:48 -0700221 void reset(const SkIRect& bounds) {
222 SkASSERT(!fPaint);
223 SkASSERT(!fNext);
224 SkASSERT(fDevice);
225 fClip.setRect(bounds);
226 }
227
reed@google.com045e62d2011-10-24 12:19:46 +0000228 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
229 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000230 int x = fDevice->getOrigin().x();
231 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 int width = fDevice->width();
233 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000234
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 if ((x | y) == 0) {
236 fMatrix = &totalMatrix;
237 fClip = totalClip;
238 } else {
239 fMatrixStorage = totalMatrix;
240 fMatrixStorage.postTranslate(SkIntToScalar(-x),
241 SkIntToScalar(-y));
242 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000243
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 totalClip.translate(-x, -y, &fClip);
245 }
246
reed@google.com045e62d2011-10-24 12:19:46 +0000247 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000250
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000252 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkRegion::kDifference_Op);
254 }
reed@google.com4b226022011-01-11 18:32:13 +0000255
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000256 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258#ifdef SK_DEBUG
259 if (!fClip.isEmpty()) {
260 SkIRect deviceR;
261 deviceR.set(0, 0, width, height);
262 SkASSERT(deviceR.contains(fClip.getBounds()));
263 }
264#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000265 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266};
267
268/* This is the record we keep for each save/restore level in the stack.
269 Since a level optionally copies the matrix and/or stack, we have pointers
270 for these fields. If the value is copied for this level, the copy is
271 stored in the ...Storage field, and the pointer points to that. If the
272 value is not copied for this level, we ignore ...Storage, and just point
273 at the corresponding value in the previous level in the stack.
274*/
275class SkCanvas::MCRec {
276public:
reed1f836ee2014-07-07 07:49:34 -0700277 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700278 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 /* If there are any layers in the stack, this points to the top-most
280 one that is at or below this level in the stack (so we know what
281 bitmap/device to draw into from this level. This value is NOT
282 reference counted, since the real owner is either our fLayer field,
283 or a previous one in a lower level.)
284 */
reed2ff1fce2014-12-11 07:07:37 -0800285 DeviceCM* fTopLayer;
286 SkRasterClip fRasterClip;
287 SkMatrix fMatrix;
288 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289
reedd9544982014-09-09 18:46:22 -0700290 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700291 fFilter = nullptr;
292 fLayer = nullptr;
293 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800294 fMatrix.reset();
295 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700296
reedd9544982014-09-09 18:46:22 -0700297 // don't bother initializing fNext
298 inc_rec();
299 }
reed2ff1fce2014-12-11 07:07:37 -0800300 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700301 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700302 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700303 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800304 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 // don't bother initializing fNext
307 inc_rec();
308 }
309 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000310 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700311 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 dec_rec();
313 }
mtkleinfeaadee2015-04-08 11:25:48 -0700314
315 void reset(const SkIRect& bounds) {
316 SkASSERT(fLayer);
317 SkASSERT(fDeferredSaveCount == 0);
318
319 fMatrix.reset();
320 fRasterClip.setRect(bounds);
321 fLayer->reset(bounds);
322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323};
324
325class SkDrawIter : public SkDraw {
326public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000327 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000328 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000329 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 canvas->updateDeviceCMCache();
331
reed687fa1c2015-04-07 08:00:56 -0700332 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000334 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 }
reed@google.com4b226022011-01-11 18:32:13 +0000336
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 bool next() {
338 // skip over recs with empty clips
339 if (fSkipEmptyClips) {
340 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
341 fCurrLayer = fCurrLayer->fNext;
342 }
343 }
344
reed@google.comf68c5e22012-02-24 16:38:58 +0000345 const DeviceCM* rec = fCurrLayer;
346 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
348 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000349 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
350 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700352 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700353 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700354 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000356 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357
358 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700359 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000360
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 return true;
362 }
363 return false;
364 }
reed@google.com4b226022011-01-11 18:32:13 +0000365
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000366 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000367 int getX() const { return fDevice->getOrigin().x(); }
368 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 const SkMatrix& getMatrix() const { return *fMatrix; }
370 const SkRegion& getClip() const { return *fClip; }
371 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000372
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373private:
374 SkCanvas* fCanvas;
375 const DeviceCM* fCurrLayer;
376 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 SkBool8 fSkipEmptyClips;
378
379 typedef SkDraw INHERITED;
380};
381
382/////////////////////////////////////////////////////////////////////////////
383
reeddbc3cef2015-04-29 12:18:57 -0700384static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
385 return lazy->isValid() ? lazy->get() : lazy->set(orig);
386}
387
388/**
389 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700390 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700391 */
392static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700393 SkImageFilter* imgf = paint.getImageFilter();
394 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700395 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700396 }
397
398 SkColorFilter* imgCF;
399 if (!imgf->asAColorFilter(&imgCF)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700400 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700401 }
402
403 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700404 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700405 // there is no existing paint colorfilter, so we can just return the imagefilter's
406 return imgCF;
407 }
408
409 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
410 // and we need to combine them into a single colorfilter.
411 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
412 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700413}
414
senorblanco87e066e2015-10-28 11:23:36 -0700415/**
416 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
417 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
418 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
419 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
420 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
421 * conservative "effective" bounds based on the settings in the paint... with one exception. This
422 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
423 * deliberately ignored.
424 */
425static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
426 const SkRect& rawBounds,
427 SkRect* storage) {
428 SkPaint tmpUnfiltered(paint);
429 tmpUnfiltered.setImageFilter(nullptr);
430 if (tmpUnfiltered.canComputeFastBounds()) {
431 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
432 } else {
433 return rawBounds;
434 }
435}
436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437class AutoDrawLooper {
438public:
senorblanco87e066e2015-10-28 11:23:36 -0700439 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
440 // paint. It's used to determine the size of the offscreen layer for filters.
441 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700442 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000443 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700444 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000445 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700447 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700449 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000450 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451
reeddbc3cef2015-04-29 12:18:57 -0700452 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
453 if (simplifiedCF) {
454 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
455 paint->setColorFilter(simplifiedCF)->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700456 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700457 fPaint = paint;
458 }
459
460 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700461 /**
462 * We implement ImageFilters for a given draw by creating a layer, then applying the
463 * imagefilter to the pixels of that layer (its backing surface/image), and then
464 * we call restore() to xfer that layer to the main canvas.
465 *
466 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
467 * 2. Generate the src pixels:
468 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
469 * return (fPaint). We then draw the primitive (using srcover) into a cleared
470 * buffer/surface.
471 * 3. Restore the layer created in #1
472 * The imagefilter is passed the buffer/surface from the layer (now filled with the
473 * src pixels of the primitive). It returns a new "filtered" buffer, which we
474 * draw onto the previous layer using the xfermode from the original paint.
475 */
reed@google.com8926b162012-03-23 15:36:36 +0000476 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700477 tmp.setImageFilter(fPaint->getImageFilter());
478 tmp.setXfermode(fPaint->getXfermode());
senorblanco87e066e2015-10-28 11:23:36 -0700479 SkRect storage;
480 if (rawBounds) {
481 // Make rawBounds include all paint outsets except for those due to image filters.
482 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
483 }
senorblanco87e066e2015-10-28 11:23:36 -0700484 (void)canvas->internalSaveLayer(rawBounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700485 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700486 fTempLayerForImageFilter = true;
487 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000488 }
489
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000490 if (SkDrawLooper* looper = paint.getLooper()) {
491 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
492 looper->contextSize());
493 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000494 fIsSimple = false;
495 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700496 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000497 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700498 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000499 }
piotaixrb5fae932014-09-24 13:03:30 -0700500
reed4a8126e2014-09-22 07:29:03 -0700501 uint32_t oldFlags = paint.getFlags();
502 fNewPaintFlags = filter_paint_flags(props, oldFlags);
503 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700504 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700505 paint->setFlags(fNewPaintFlags);
506 fPaint = paint;
507 // if we're not simple, doNext() will take care of calling setFlags()
508 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000509 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700512 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000513 fCanvas->internalRestore();
514 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000515 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000517
reed@google.com4e2b3d32011-04-07 14:18:59 +0000518 const SkPaint& paint() const {
519 SkASSERT(fPaint);
520 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000522
reed@google.com129ec222012-05-15 13:24:09 +0000523 bool next(SkDrawFilter::Type drawType) {
524 if (fDone) {
525 return false;
526 } else if (fIsSimple) {
527 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000528 return !fPaint->nothingToDraw();
529 } else {
530 return this->doNext(drawType);
531 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000532 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534private:
reeddbc3cef2015-04-29 12:18:57 -0700535 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
536 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000537 SkCanvas* fCanvas;
538 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000539 SkDrawFilter* fFilter;
540 const SkPaint* fPaint;
541 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700542 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700543 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000544 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000545 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000546 SkDrawLooper::Context* fLooperContext;
547 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000548
549 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550};
551
reed@google.com129ec222012-05-15 13:24:09 +0000552bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700553 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000554 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700555 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000556
reeddbc3cef2015-04-29 12:18:57 -0700557 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
558 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700559 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000560
reed5c476fb2015-04-20 08:04:21 -0700561 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700562 paint->setImageFilter(nullptr);
563 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000564 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000565
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000566 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000567 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000568 return false;
569 }
570 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000571 if (!fFilter->filter(paint, drawType)) {
572 fDone = true;
573 return false;
574 }
halcanary96fcdcc2015-08-27 07:41:13 -0700575 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000576 // no looper means we only draw once
577 fDone = true;
578 }
579 }
580 fPaint = paint;
581
582 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000583 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000584 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000585 }
586
587 // call this after any possible paint modifiers
588 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700589 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000590 return false;
591 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000592 return true;
593}
594
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595////////// macros to place around the internal draw calls //////////////////
596
reed262a71b2015-12-05 13:07:27 -0800597#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
598 this->predrawNotify(); \
599 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
600 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
601 SkDrawIter iter(this);
602
603
reed@google.com8926b162012-03-23 15:36:36 +0000604#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000605 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700606 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000607 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000608 SkDrawIter iter(this);
609
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000610#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000611 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700612 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000613 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000615
reedc83a2972015-07-16 07:40:45 -0700616#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
617 this->predrawNotify(bounds, &paint, auxOpaque); \
618 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
619 while (looper.next(type)) { \
620 SkDrawIter iter(this);
621
reed@google.com4e2b3d32011-04-07 14:18:59 +0000622#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623
624////////////////////////////////////////////////////////////////////////////
625
mtkleinfeaadee2015-04-08 11:25:48 -0700626void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
627 this->restoreToCount(1);
628 fCachedLocalClipBounds.setEmpty();
629 fCachedLocalClipBoundsDirty = true;
630 fClipStack->reset();
631 fMCRec->reset(bounds);
632
633 // We're peering through a lot of structs here. Only at this scope do we
634 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
635 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
636}
637
reedd9544982014-09-09 18:46:22 -0700638SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800639 if (device && device->forceConservativeRasterClip()) {
640 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
641 }
642 // Since init() is only called once by our constructors, it is safe to perform this
643 // const-cast.
644 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
645
reed@google.comc0784db2013-12-13 21:16:12 +0000646 fCachedLocalClipBounds.setEmpty();
647 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000648 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000649 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700650 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800651 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700652 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653
halcanary385fe4d2015-08-26 13:07:48 -0700654 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700655
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700657 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658
reeda499f902015-05-01 09:34:31 -0700659 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
660 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700661 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664
halcanary96fcdcc2015-08-27 07:41:13 -0700665 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000666
reedf92c8662014-08-18 08:02:43 -0700667 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700668 // The root device and the canvas should always have the same pixel geometry
669 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700670 device->onAttachToCanvas(this);
671 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800672 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700673 }
674 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675}
676
reed@google.comcde92112011-07-06 20:00:52 +0000677SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000678 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700679 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800680 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000681{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000682 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000683
halcanary96fcdcc2015-08-27 07:41:13 -0700684 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000685}
686
reedd9544982014-09-09 18:46:22 -0700687static SkBitmap make_nopixels(int width, int height) {
688 SkBitmap bitmap;
689 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
690 return bitmap;
691}
692
693class SkNoPixelsBitmapDevice : public SkBitmapDevice {
694public:
robertphillipsfcf78292015-06-19 11:49:52 -0700695 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
696 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800697 {
698 this->setOrigin(bounds.x(), bounds.y());
699 }
reedd9544982014-09-09 18:46:22 -0700700
701private:
piotaixrb5fae932014-09-24 13:03:30 -0700702
reedd9544982014-09-09 18:46:22 -0700703 typedef SkBitmapDevice INHERITED;
704};
705
reed96a857e2015-01-25 10:33:58 -0800706SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000707 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800708 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800709 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000710{
711 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700712
halcanary385fe4d2015-08-26 13:07:48 -0700713 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
714 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700715}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000716
reed78e27682014-11-19 08:04:34 -0800717SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700718 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700719 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800720 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700721{
722 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700723
halcanary385fe4d2015-08-26 13:07:48 -0700724 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700725}
726
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000727SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000728 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700729 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800730 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000731{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700733
reedd9544982014-09-09 18:46:22 -0700734 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735}
736
robertphillipsfcf78292015-06-19 11:49:52 -0700737SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
738 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700739 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800740 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700741{
742 inc_canvas();
743
744 this->init(device, flags);
745}
746
reed4a8126e2014-09-22 07:29:03 -0700747SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700748 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700749 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800750 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700751{
752 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700753
halcanary385fe4d2015-08-26 13:07:48 -0700754 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700755 this->init(device, kDefault_InitFlags);
756}
reed29c857d2014-09-21 10:25:07 -0700757
reed4a8126e2014-09-22 07:29:03 -0700758SkCanvas::SkCanvas(const SkBitmap& bitmap)
759 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
760 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800761 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700762{
763 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700764
halcanary385fe4d2015-08-26 13:07:48 -0700765 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700766 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767}
768
769SkCanvas::~SkCanvas() {
770 // free up the contents of our deque
771 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000772
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 this->internalRestore(); // restore the last, since we're going away
774
halcanary385fe4d2015-08-26 13:07:48 -0700775 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000776
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 dec_canvas();
778}
779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780SkDrawFilter* SkCanvas::getDrawFilter() const {
781 return fMCRec->fFilter;
782}
783
784SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700785 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
787 return filter;
788}
789
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000790SkMetaData& SkCanvas::getMetaData() {
791 // metadata users are rare, so we lazily allocate it. If that changes we
792 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700793 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000794 fMetaData = new SkMetaData;
795 }
796 return *fMetaData;
797}
798
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799///////////////////////////////////////////////////////////////////////////////
800
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000801void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000802 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000803 if (device) {
804 device->flush();
805 }
806}
807
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000808SkISize SkCanvas::getTopLayerSize() const {
809 SkBaseDevice* d = this->getTopDevice();
810 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
811}
812
813SkIPoint SkCanvas::getTopLayerOrigin() const {
814 SkBaseDevice* d = this->getTopDevice();
815 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
816}
817
818SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000819 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000820 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
821}
822
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000823SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000825 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 SkASSERT(rec && rec->fLayer);
827 return rec->fLayer->fDevice;
828}
829
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000830SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000831 if (updateMatrixClip) {
832 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
833 }
reed@google.com9266fed2011-03-30 00:18:03 +0000834 return fMCRec->fTopLayer->fDevice;
835}
836
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000837bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
838 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
839 return false;
840 }
841
842 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700843 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700844 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000845 return false;
846 }
847 weAllocated = true;
848 }
849
reedcf01e312015-05-23 19:14:51 -0700850 SkAutoPixmapUnlock unlocker;
851 if (bitmap->requestLock(&unlocker)) {
852 const SkPixmap& pm = unlocker.pixmap();
853 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
854 return true;
855 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000856 }
857
858 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700859 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000860 }
861 return false;
862}
reed@google.com51df9e32010-12-23 19:29:18 +0000863
bsalomon@google.comc6980972011-11-02 19:57:21 +0000864bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000865 SkIRect r = srcRect;
866 const SkISize size = this->getBaseLayerSize();
867 if (!r.intersect(0, 0, size.width(), size.height())) {
868 bitmap->reset();
869 return false;
870 }
871
reed84825042014-09-02 12:50:45 -0700872 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000873 // bitmap will already be reset.
874 return false;
875 }
876 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
877 bitmap->reset();
878 return false;
879 }
880 return true;
881}
882
reed96472de2014-12-10 09:53:42 -0800883bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000884 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000885 if (!device) {
886 return false;
887 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000888 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800889
reed96472de2014-12-10 09:53:42 -0800890 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
891 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000892 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000893 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000894
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000895 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800896 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000897}
898
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000899bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
900 if (bitmap.getTexture()) {
901 return false;
902 }
reedcf01e312015-05-23 19:14:51 -0700903
904 SkAutoPixmapUnlock unlocker;
905 if (bitmap.requestLock(&unlocker)) {
906 const SkPixmap& pm = unlocker.pixmap();
907 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000908 }
909 return false;
910}
911
912bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
913 int x, int y) {
914 switch (origInfo.colorType()) {
915 case kUnknown_SkColorType:
916 case kIndex_8_SkColorType:
917 return false;
918 default:
919 break;
920 }
halcanary96fcdcc2015-08-27 07:41:13 -0700921 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000922 return false;
923 }
924
925 const SkISize size = this->getBaseLayerSize();
926 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
927 if (!target.intersect(0, 0, size.width(), size.height())) {
928 return false;
929 }
930
931 SkBaseDevice* device = this->getDevice();
932 if (!device) {
933 return false;
934 }
935
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000936 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700937 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000938
939 // if x or y are negative, then we have to adjust pixels
940 if (x > 0) {
941 x = 0;
942 }
943 if (y > 0) {
944 y = 0;
945 }
946 // here x,y are either 0 or negative
947 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
948
reed4af35f32014-06-27 17:47:49 -0700949 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700950 const bool completeOverwrite = info.dimensions() == size;
951 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700952
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000953 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000954 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000955}
reed@google.com51df9e32010-12-23 19:29:18 +0000956
junov@google.com4370aed2012-01-18 16:21:08 +0000957SkCanvas* SkCanvas::canvasForDrawIter() {
958 return this;
959}
960
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961//////////////////////////////////////////////////////////////////////////////
962
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963void SkCanvas::updateDeviceCMCache() {
964 if (fDeviceCMDirty) {
965 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700966 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000968
halcanary96fcdcc2015-08-27 07:41:13 -0700969 if (nullptr == layer->fNext) { // only one layer
970 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000972 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 do {
reed687fa1c2015-04-07 08:00:56 -0700974 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700975 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 }
977 fDeviceCMDirty = false;
978 }
979}
980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981///////////////////////////////////////////////////////////////////////////////
982
reed2ff1fce2014-12-11 07:07:37 -0800983void SkCanvas::checkForDeferredSave() {
984 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800985 this->doSave();
986 }
987}
988
reedf0090cb2014-11-26 08:55:51 -0800989int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800990#ifdef SK_DEBUG
991 int count = 0;
992 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
993 for (;;) {
994 const MCRec* rec = (const MCRec*)iter.next();
995 if (!rec) {
996 break;
997 }
998 count += 1 + rec->fDeferredSaveCount;
999 }
1000 SkASSERT(count == fSaveCount);
1001#endif
1002 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001003}
1004
1005int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001006 fSaveCount += 1;
1007 fMCRec->fDeferredSaveCount += 1;
1008 return this->getSaveCount() - 1; // return our prev value
1009}
1010
1011void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001012 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001013
1014 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1015 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001016 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001017}
1018
1019void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001020 if (fMCRec->fDeferredSaveCount > 0) {
1021 SkASSERT(fSaveCount > 1);
1022 fSaveCount -= 1;
1023 fMCRec->fDeferredSaveCount -= 1;
1024 } else {
1025 // check for underflow
1026 if (fMCStack.count() > 1) {
1027 this->willRestore();
1028 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001029 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001030 this->internalRestore();
1031 this->didRestore();
1032 }
reedf0090cb2014-11-26 08:55:51 -08001033 }
1034}
1035
1036void SkCanvas::restoreToCount(int count) {
1037 // sanity check
1038 if (count < 1) {
1039 count = 1;
1040 }
mtkleinf0f14112014-12-12 08:46:25 -08001041
reedf0090cb2014-11-26 08:55:51 -08001042 int n = this->getSaveCount() - count;
1043 for (int i = 0; i < n; ++i) {
1044 this->restore();
1045 }
1046}
1047
reed2ff1fce2014-12-11 07:07:37 -08001048void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001050 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001052
reed687fa1c2015-04-07 08:00:56 -07001053 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054}
1055
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001057#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +00001059#else
1060 return true;
1061#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062}
1063
junov@chromium.orga907ac32012-02-24 21:54:07 +00001064bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -07001065 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001066 SkIRect clipBounds;
1067 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001068 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001069 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001070
reed96e657d2015-03-10 17:30:07 -07001071 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1072
senorblanco87e066e2015-10-28 11:23:36 -07001073// This is a temporary hack, until individual filters can do their own
1074// bloating, when this will be removed.
1075#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1076 SkRect storage;
1077#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001078 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001079 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco87e066e2015-10-28 11:23:36 -07001080#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1081 if (bounds && imageFilter->canComputeFastBounds()) {
1082 imageFilter->computeFastBounds(*bounds, &storage);
1083 bounds = &storage;
1084 } else {
1085 bounds = nullptr;
1086 }
1087#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001088 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001089 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001090 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001092
reed96e657d2015-03-10 17:30:07 -07001093 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 r.roundOut(&ir);
1095 // early exit if the layer's bounds are clipped out
1096 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001097 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -07001098 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001099 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001100 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001101 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 }
1103 } else { // no user bounds, so just use the clip
1104 ir = clipBounds;
1105 }
reed180aec42015-03-11 10:39:04 -07001106 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +00001108 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -07001109 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001110 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001111 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001112 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001113 }
1114
1115 if (intersection) {
1116 *intersection = ir;
1117 }
1118 return true;
1119}
1120
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001121int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -08001122 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001123 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001124 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001125 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -07001126 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001127 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001128 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001129}
1130
reed2ff1fce2014-12-11 07:07:37 -08001131int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -08001132 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001133 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001134 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001135 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -07001136 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001137 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001138 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +00001139}
1140
reed2ff1fce2014-12-11 07:07:37 -08001141void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -07001142 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +00001143#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +00001144 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001145#endif
1146
junov@chromium.orga907ac32012-02-24 21:54:07 +00001147 // do this before we create the layer. We don't call the public save() since
1148 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001149 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001150
1151 fDeviceCMDirty = true;
1152
1153 SkIRect ir;
halcanary96fcdcc2015-08-27 07:41:13 -07001154 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001155 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 }
1157
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001158 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1159 // the clipRectBounds() call above?
1160 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001161 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001162 }
1163
reed76033be2015-03-14 10:54:31 -07001164 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001165 SkPixelGeometry geo = fProps.pixelGeometry();
1166 if (paint) {
reed76033be2015-03-14 10:54:31 -07001167 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001168 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001169 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001170 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001171 }
1172 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001173 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1174 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175
reedb2db8982014-11-13 12:41:02 -08001176 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001177 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001178 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001179 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001180 }
reedb2db8982014-11-13 12:41:02 -08001181
reed61f501f2015-04-29 08:34:00 -07001182 bool forceSpriteOnRestore = false;
1183 {
reeddaa57bf2015-05-15 10:39:17 -07001184 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed61f501f2015-04-29 08:34:00 -07001185 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo);
1186 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001187 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001188 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001189 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1190 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001191 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001192 SkErrorInternals::SetError(kInternalError_SkError,
1193 "Unable to create device for layer.");
1194 return;
1195 }
1196 forceSpriteOnRestore = true;
1197 }
1198 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001199 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001200
reed@google.com6f8f2922011-03-04 22:27:10 +00001201 device->setOrigin(ir.fLeft, ir.fTop);
halcanary385fe4d2015-08-26 13:07:48 -07001202 DeviceCM* layer =
1203 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 device->unref();
1205
1206 layer->fNext = fMCRec->fTopLayer;
1207 fMCRec->fLayer = layer;
1208 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209}
1210
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001211int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1212 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1213}
1214
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1216 SaveFlags flags) {
1217 if (0xFF == alpha) {
halcanary96fcdcc2015-08-27 07:41:13 -07001218 return this->saveLayer(bounds, nullptr, flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 } else {
1220 SkPaint tmpPaint;
1221 tmpPaint.setAlpha(alpha);
1222 return this->saveLayer(bounds, &tmpPaint, flags);
1223 }
1224}
1225
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226void SkCanvas::internalRestore() {
1227 SkASSERT(fMCStack.count() != 0);
1228
1229 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001230 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231
reed687fa1c2015-04-07 08:00:56 -07001232 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001233
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001234 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 DeviceCM* layer = fMCRec->fLayer; // may be null
1236 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001237 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238
1239 // now do the normal restore()
1240 fMCRec->~MCRec(); // balanced in save()
1241 fMCStack.pop_back();
1242 fMCRec = (MCRec*)fMCStack.back();
1243
1244 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1245 since if we're being recorded, we don't want to record this (the
1246 recorder will have already recorded the restore).
1247 */
bsalomon49f085d2014-09-05 13:34:00 -07001248 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001250 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001251 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001252 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001253 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001255 delete layer;
reedb679ca82015-04-07 04:40:48 -07001256 } else {
1257 // we're at the root
reeda499f902015-05-01 09:34:31 -07001258 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001259 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262}
1263
reed4a8126e2014-09-22 07:29:03 -07001264SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001265 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001266 props = &fProps;
1267 }
1268 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001269}
1270
reed4a8126e2014-09-22 07:29:03 -07001271SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001272 SkBaseDevice* dev = this->getDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001273 return dev ? dev->newSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001274}
1275
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001276SkImageInfo SkCanvas::imageInfo() const {
1277 SkBaseDevice* dev = this->getDevice();
1278 if (dev) {
1279 return dev->imageInfo();
1280 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001281 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001282 }
1283}
1284
1285const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001286 SkPixmap pmap;
1287 if (!this->onPeekPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001288 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001289 }
1290 if (info) {
1291 *info = pmap.info();
1292 }
1293 if (rowBytes) {
1294 *rowBytes = pmap.rowBytes();
1295 }
1296 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001297}
1298
reed884e97c2015-05-26 11:31:54 -07001299bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001300 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001301 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001302}
1303
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001304void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001305 SkPixmap pmap;
1306 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001307 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001308 }
1309 if (info) {
1310 *info = pmap.info();
1311 }
1312 if (rowBytes) {
1313 *rowBytes = pmap.rowBytes();
1314 }
1315 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001316 *origin = this->getTopDevice(false)->getOrigin();
1317 }
reed884e97c2015-05-26 11:31:54 -07001318 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001319}
1320
reed884e97c2015-05-26 11:31:54 -07001321bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001322 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001323 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001324}
1325
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001326SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1327 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -07001328 if (nullptr == fAddr) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001329 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001330 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001331 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001332 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001333 if (!canvas->readPixels(&fBitmap, 0, 0)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001334 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001335 }
1336 fAddr = fBitmap.getPixels();
1337 fRowBytes = fBitmap.rowBytes();
1338 }
1339 SkASSERT(fAddr); // success
1340}
1341
1342bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1343 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001344 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001345 } else {
1346 bitmap->reset();
1347 return false;
1348 }
1349}
1350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001353void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001354 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001356 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 paint = &tmp;
1358 }
reed@google.com4b226022011-01-11 18:32:13 +00001359
reed@google.com8926b162012-03-23 15:36:36 +00001360 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001362 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001363 paint = &looper.paint();
1364 SkImageFilter* filter = paint->getImageFilter();
1365 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001366 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001367 SkImageFilter::DeviceProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001368 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001369 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001370 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001371 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001372 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001373 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001374 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reedc9b5f8b2015-10-22 13:20:20 -07001375 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
1376 SkImageFilter::kApprox_SizeConstraint);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001377 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001378 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001379 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001380 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1381 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001382 }
reed61f501f2015-04-29 08:34:00 -07001383 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001384 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001385 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001386 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001387 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001388 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001390 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391}
1392
reed41af9662015-01-05 07:49:08 -08001393void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001394 if (gTreatSpriteAsBitmap) {
1395 this->save();
1396 this->resetMatrix();
1397 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1398 this->restore();
1399 return;
1400 }
1401
danakj9881d632014-11-26 12:41:06 -08001402 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001403 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001404 return;
1405 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001406 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001407
reed@google.com8926b162012-03-23 15:36:36 +00001408 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001409 if (nullptr == paint) {
reed@google.com8926b162012-03-23 15:36:36 +00001410 paint = &tmp;
1411 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001412
reed@google.com8926b162012-03-23 15:36:36 +00001413 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001414
reed@google.com8926b162012-03-23 15:36:36 +00001415 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08001416 const SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1417 iter.fDevice->drawBitmapAsSprite(iter, bitmap, pos.x(), pos.y(), looper.paint());
reed@google.com8926b162012-03-23 15:36:36 +00001418 }
1419 LOOPER_END
1420}
1421
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001423void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001424 SkMatrix m;
1425 m.setTranslate(dx, dy);
1426 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427}
1428
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001429void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001430 SkMatrix m;
1431 m.setScale(sx, sy);
1432 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433}
1434
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001435void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001436 SkMatrix m;
1437 m.setRotate(degrees);
1438 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439}
1440
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001441void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001442 SkMatrix m;
1443 m.setSkew(sx, sy);
1444 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001445}
1446
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001447void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001448 if (matrix.isIdentity()) {
1449 return;
1450 }
1451
reed2ff1fce2014-12-11 07:07:37 -08001452 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001454 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001455 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001456
1457 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001458}
1459
reed86a17e72015-05-14 12:25:22 -07001460void SkCanvas::setMatrix(const SkMatrix& matrix) {
1461 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001463 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001464 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001465 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468void SkCanvas::resetMatrix() {
1469 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001470
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471 matrix.reset();
1472 this->setMatrix(matrix);
1473}
1474
1475//////////////////////////////////////////////////////////////////////////////
1476
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001477void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001478 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001479 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1480 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001481}
1482
1483void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001484#ifdef SK_ENABLE_CLIP_QUICKREJECT
1485 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001486 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001487 return false;
1488 }
1489
reed@google.com3b3e8952012-08-16 20:53:31 +00001490 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001491 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001492 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001493
reed687fa1c2015-04-07 08:00:56 -07001494 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001495 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001496 }
1497 }
1498#endif
1499
bsalomonac8cabd2015-11-20 18:53:07 -08001500 if (!fAllowSoftClip) {
1501 edgeStyle = kHard_ClipEdgeStyle;
1502 }
reed90ba0952015-11-20 13:42:47 -08001503
reedc64eff52015-11-21 12:39:45 -08001504 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1505 SkRect devR;
1506 if (rectStaysRect) {
1507 fMCRec->fMatrix.mapRect(&devR, rect);
1508 }
bsalomonac8cabd2015-11-20 18:53:07 -08001509
reedc64eff52015-11-21 12:39:45 -08001510 // Check if we can quick-accept the clip call (and do nothing)
1511 //
1512 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1513 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1514 // might allow lazy save/restores to eliminate entire save/restore blocks.
1515 //
1516 if (SkRegion::kIntersect_Op == op &&
1517 kHard_ClipEdgeStyle == edgeStyle
1518 && rectStaysRect)
1519 {
1520 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1521#if 0
1522 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1523 rect.left(), rect.top(), rect.right(), rect.bottom());
1524#endif
1525 return;
1526 }
1527 }
1528
1529 AutoValidateClip avc(this);
1530
1531 fDeviceCMDirty = true;
1532 fCachedLocalClipBoundsDirty = true;
1533
1534 if (rectStaysRect) {
1535 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1536 fClipStack->clipDevRect(devR, op, isAA);
1537 fMCRec->fRasterClip.op(devR, this->getBaseLayerSize(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001539 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001540 // and clip against that, since it can handle any matrix. However, to
1541 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1542 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543 SkPath path;
1544
1545 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001546 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547 }
1548}
1549
reed73e714e2014-09-04 09:02:23 -07001550static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1551 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001552 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001553}
1554
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001555void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001556 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001557 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001558 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001559 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1560 } else {
1561 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001562 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001564
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001566 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001567 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001568 AutoValidateClip avc(this);
1569
1570 fDeviceCMDirty = true;
1571 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001572 if (!fAllowSoftClip) {
1573 edgeStyle = kHard_ClipEdgeStyle;
1574 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001575
reed687fa1c2015-04-07 08:00:56 -07001576 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001577
robertphillips125f19a2015-11-23 09:00:05 -08001578 fMCRec->fRasterClip.op(transformedRRect, this->getBaseLayerSize(), op,
1579 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001580 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001581 }
1582
1583 SkPath path;
1584 path.addRRect(rrect);
1585 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001586 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001587}
1588
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001589void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001590 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001591 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001592
1593 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1594 SkRect r;
1595 if (path.isRect(&r)) {
1596 this->onClipRect(r, op, edgeStyle);
1597 return;
1598 }
1599 SkRRect rrect;
1600 if (path.isOval(&r)) {
1601 rrect.setOval(r);
1602 this->onClipRRect(rrect, op, edgeStyle);
1603 return;
1604 }
1605 if (path.isRRect(&rrect)) {
1606 this->onClipRRect(rrect, op, edgeStyle);
1607 return;
1608 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001609 }
robertphillips39f05382015-11-24 09:30:12 -08001610
1611 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001612}
1613
1614void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001615#ifdef SK_ENABLE_CLIP_QUICKREJECT
1616 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001617 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001618 return false;
1619 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001620
reed@google.com3b3e8952012-08-16 20:53:31 +00001621 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001622 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001623 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001624
reed687fa1c2015-04-07 08:00:56 -07001625 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001626 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001627 }
1628 }
1629#endif
1630
reed@google.com5c3d1472011-02-22 19:12:23 +00001631 AutoValidateClip avc(this);
1632
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001634 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635 if (!fAllowSoftClip) {
1636 edgeStyle = kHard_ClipEdgeStyle;
1637 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638
1639 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001640 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641
reed@google.comfe701122011-11-08 19:41:23 +00001642 // Check if the transfomation, or the original path itself
1643 // made us empty. Note this can also happen if we contained NaN
1644 // values. computing the bounds detects this, and will set our
1645 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1646 if (devPath.getBounds().isEmpty()) {
1647 // resetting the path will remove any NaN or other wanky values
1648 // that might upset our scan converter.
1649 devPath.reset();
1650 }
1651
reed@google.com5c3d1472011-02-22 19:12:23 +00001652 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001653 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001654
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001655 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001656 bool clipIsAA = getClipStack()->asPath(&devPath);
1657 if (clipIsAA) {
1658 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001659 }
fmalita1a481fe2015-02-04 07:39:34 -08001660
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001661 op = SkRegion::kReplace_Op;
1662 }
1663
reed73e714e2014-09-04 09:02:23 -07001664 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665}
1666
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001667void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001668 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001669 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001670}
1671
1672void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001673 AutoValidateClip avc(this);
1674
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001676 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677
reed@google.com5c3d1472011-02-22 19:12:23 +00001678 // todo: signal fClipStack that we have a region, and therefore (I guess)
1679 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001680 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001681
reed1f836ee2014-07-07 07:49:34 -07001682 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683}
1684
reed@google.com819c9212011-02-23 18:56:55 +00001685#ifdef SK_DEBUG
1686void SkCanvas::validateClip() const {
1687 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001688 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001689 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001690 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001691 return;
1692 }
1693
reed@google.com819c9212011-02-23 18:56:55 +00001694 SkIRect ir;
1695 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001696 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001697
reed687fa1c2015-04-07 08:00:56 -07001698 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001699 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001700 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001701 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001702 case SkClipStack::Element::kRect_Type:
1703 element->getRect().round(&ir);
1704 tmpClip.op(ir, element->getOp());
1705 break;
1706 case SkClipStack::Element::kEmpty_Type:
1707 tmpClip.setEmpty();
1708 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001709 default: {
1710 SkPath path;
1711 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001712 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001713 break;
1714 }
reed@google.com819c9212011-02-23 18:56:55 +00001715 }
1716 }
reed@google.com819c9212011-02-23 18:56:55 +00001717}
1718#endif
1719
reed@google.com90c07ea2012-04-13 13:50:27 +00001720void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001721 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001722 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001723
halcanary96fcdcc2015-08-27 07:41:13 -07001724 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001725 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001726 }
1727}
1728
reed@google.com5c3d1472011-02-22 19:12:23 +00001729///////////////////////////////////////////////////////////////////////////////
1730
reed@google.com754de5f2014-02-24 19:38:20 +00001731bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001732 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001733}
1734
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001735bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001736 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001737}
1738
reed@google.com3b3e8952012-08-16 20:53:31 +00001739bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001740 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001741 return true;
1742
reed1f836ee2014-07-07 07:49:34 -07001743 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 return true;
1745 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746
reed1f836ee2014-07-07 07:49:34 -07001747 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001748 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001749 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001750 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001751 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001752 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001753
reed@android.coma380ae42009-07-21 01:17:02 +00001754 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001755 // TODO: should we use | instead, or compare all 4 at once?
1756 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001757 return true;
1758 }
reed@google.comc0784db2013-12-13 21:16:12 +00001759 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001760 return true;
1761 }
1762 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764}
1765
reed@google.com3b3e8952012-08-16 20:53:31 +00001766bool SkCanvas::quickReject(const SkPath& path) const {
1767 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768}
1769
reed@google.com3b3e8952012-08-16 20:53:31 +00001770bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001771 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001772 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773 return false;
1774 }
1775
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001776 SkMatrix inverse;
1777 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001778 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001779 if (bounds) {
1780 bounds->setEmpty();
1781 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001782 return false;
1783 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784
bsalomon49f085d2014-09-05 13:34:00 -07001785 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001786 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001787 // adjust it outwards in case we are antialiasing
1788 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001789
reed@google.com8f4d2302013-12-17 16:44:46 +00001790 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1791 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 inverse.mapRect(bounds, r);
1793 }
1794 return true;
1795}
1796
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001797bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001798 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001799 if (clip.isEmpty()) {
1800 if (bounds) {
1801 bounds->setEmpty();
1802 }
1803 return false;
1804 }
1805
bsalomon49f085d2014-09-05 13:34:00 -07001806 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001807 *bounds = clip.getBounds();
1808 }
1809 return true;
1810}
1811
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001813 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814}
1815
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001816const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001817 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001818}
1819
reed@google.com9c135db2014-03-12 18:28:35 +00001820GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1821 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001822 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001823}
1824
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001825GrContext* SkCanvas::getGrContext() {
1826#if SK_SUPPORT_GPU
1827 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001828 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001829 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001830 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001831 return renderTarget->getContext();
1832 }
1833 }
1834#endif
1835
halcanary96fcdcc2015-08-27 07:41:13 -07001836 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001837
1838}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001839
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001840void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1841 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001842 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001843 if (outer.isEmpty()) {
1844 return;
1845 }
1846 if (inner.isEmpty()) {
1847 this->drawRRect(outer, paint);
1848 return;
1849 }
1850
1851 // We don't have this method (yet), but technically this is what we should
1852 // be able to assert...
1853 // SkASSERT(outer.contains(inner));
1854 //
1855 // For now at least check for containment of bounds
1856 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1857
1858 this->onDrawDRRect(outer, inner, paint);
1859}
1860
reed41af9662015-01-05 07:49:08 -08001861// These need to stop being virtual -- clients need to override the onDraw... versions
1862
1863void SkCanvas::drawPaint(const SkPaint& paint) {
1864 this->onDrawPaint(paint);
1865}
1866
1867void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1868 this->onDrawRect(r, paint);
1869}
1870
1871void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1872 this->onDrawOval(r, paint);
1873}
1874
1875void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1876 this->onDrawRRect(rrect, paint);
1877}
1878
1879void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1880 this->onDrawPoints(mode, count, pts, paint);
1881}
1882
1883void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1884 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1885 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1886 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1887 indices, indexCount, paint);
1888}
1889
1890void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1891 this->onDrawPath(path, paint);
1892}
1893
reeda85d4d02015-05-06 12:56:48 -07001894void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1895 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001896}
1897
reede47829b2015-08-06 10:02:53 -07001898void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1899 const SkPaint* paint, SrcRectConstraint constraint) {
1900 if (dst.isEmpty() || src.isEmpty()) {
1901 return;
1902 }
1903 this->onDrawImageRect(image, &src, dst, paint, constraint);
1904}
reed41af9662015-01-05 07:49:08 -08001905
reed84984ef2015-07-17 07:09:43 -07001906void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1907 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001908 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001909}
1910
reede47829b2015-08-06 10:02:53 -07001911void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1912 SrcRectConstraint constraint) {
1913 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1914 constraint);
1915}
reede47829b2015-08-06 10:02:53 -07001916
reed4c21dc52015-06-25 12:32:03 -07001917void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1918 const SkPaint* paint) {
1919 if (dst.isEmpty()) {
1920 return;
1921 }
1922 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001923 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001924 }
1925 this->onDrawImageNine(image, center, dst, paint);
1926}
1927
reed41af9662015-01-05 07:49:08 -08001928void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001929 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001930 return;
1931 }
reed41af9662015-01-05 07:49:08 -08001932 this->onDrawBitmap(bitmap, dx, dy, paint);
1933}
1934
reede47829b2015-08-06 10:02:53 -07001935void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001936 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001937 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001938 return;
1939 }
reede47829b2015-08-06 10:02:53 -07001940 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001941}
1942
reed84984ef2015-07-17 07:09:43 -07001943void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1944 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001945 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001946}
1947
reede47829b2015-08-06 10:02:53 -07001948void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1949 SrcRectConstraint constraint) {
1950 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1951 constraint);
1952}
reede47829b2015-08-06 10:02:53 -07001953
reed41af9662015-01-05 07:49:08 -08001954void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1955 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001956 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001957 return;
1958 }
reed4c21dc52015-06-25 12:32:03 -07001959 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001960 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001961 }
reed41af9662015-01-05 07:49:08 -08001962 this->onDrawBitmapNine(bitmap, center, dst, paint);
1963}
1964
1965void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001966 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001967 return;
1968 }
reed41af9662015-01-05 07:49:08 -08001969 this->onDrawSprite(bitmap, left, top, paint);
1970}
1971
reed71c3c762015-06-24 10:29:17 -07001972void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1973 const SkColor colors[], int count, SkXfermode::Mode mode,
1974 const SkRect* cull, const SkPaint* paint) {
1975 if (count <= 0) {
1976 return;
1977 }
1978 SkASSERT(atlas);
1979 SkASSERT(xform);
1980 SkASSERT(tex);
1981 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1982}
1983
reede47829b2015-08-06 10:02:53 -07001984void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1985 const SkPaint* paint, SrcRectConstraint constraint) {
1986 if (src) {
1987 this->drawImageRect(image, *src, dst, paint, constraint);
1988 } else {
1989 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
1990 dst, paint, constraint);
1991 }
1992}
1993void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1994 const SkPaint* paint, SrcRectConstraint constraint) {
1995 if (src) {
1996 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
1997 } else {
1998 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
1999 dst, paint, constraint);
2000 }
2001}
2002
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003//////////////////////////////////////////////////////////////////////////////
2004// These are the virtual drawing methods
2005//////////////////////////////////////////////////////////////////////////////
2006
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002007void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002008 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002009 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2010 }
2011}
2012
reed41af9662015-01-05 07:49:08 -08002013void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002014 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002015 this->internalDrawPaint(paint);
2016}
2017
2018void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002019 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002020
2021 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002022 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002023 }
2024
reed@google.com4e2b3d32011-04-07 14:18:59 +00002025 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026}
2027
reed41af9662015-01-05 07:49:08 -08002028void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2029 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002030 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002031 if ((long)count <= 0) {
2032 return;
2033 }
2034
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002035 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002036 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002037 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002038 // special-case 2 points (common for drawing a single line)
2039 if (2 == count) {
2040 r.set(pts[0], pts[1]);
2041 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002042 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002043 }
senorblanco87e066e2015-10-28 11:23:36 -07002044 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2045 return;
2046 }
2047 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002048 }
reed@google.coma584aed2012-05-16 14:06:02 +00002049
halcanary96fcdcc2015-08-27 07:41:13 -07002050 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002052 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002053
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002055 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056 }
reed@google.com4b226022011-01-11 18:32:13 +00002057
reed@google.com4e2b3d32011-04-07 14:18:59 +00002058 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059}
2060
reed41af9662015-01-05 07:49:08 -08002061void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002062 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002063 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002064 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002065 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002066 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2067 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2068 SkRect tmp(r);
2069 tmp.sort();
2070
senorblanco87e066e2015-10-28 11:23:36 -07002071 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2072 return;
2073 }
2074 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 }
reed@google.com4b226022011-01-11 18:32:13 +00002076
reedc83a2972015-07-16 07:40:45 -07002077 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078
2079 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002080 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 }
2082
reed@google.com4e2b3d32011-04-07 14:18:59 +00002083 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084}
2085
reed41af9662015-01-05 07:49:08 -08002086void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002087 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002088 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002089 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002090 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002091 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2092 return;
2093 }
2094 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002095 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002096
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002097 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002098
2099 while (iter.next()) {
2100 iter.fDevice->drawOval(iter, oval, looper.paint());
2101 }
2102
2103 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002104}
2105
reed41af9662015-01-05 07:49:08 -08002106void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002107 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002108 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002109 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002110 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002111 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2112 return;
2113 }
2114 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002115 }
2116
2117 if (rrect.isRect()) {
2118 // call the non-virtual version
2119 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002120 return;
2121 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002122 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002123 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2124 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002125 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002126
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002127 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002128
2129 while (iter.next()) {
2130 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2131 }
2132
2133 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002134}
2135
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002136void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2137 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002138 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002139 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002140 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002141 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2142 return;
2143 }
2144 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002145 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002146
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002147 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002148
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002149 while (iter.next()) {
2150 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2151 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002152
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002153 LOOPER_END
2154}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002155
reed41af9662015-01-05 07:49:08 -08002156void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002157 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002158 if (!path.isFinite()) {
2159 return;
2160 }
2161
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002162 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002163 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002164 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002165 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002166 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2167 return;
2168 }
2169 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002171
2172 const SkRect& r = path.getBounds();
2173 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002174 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002175 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002176 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002177 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002178 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002180 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181
2182 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002183 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 }
2185
reed@google.com4e2b3d32011-04-07 14:18:59 +00002186 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187}
2188
reed262a71b2015-12-05 13:07:27 -08002189bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
2190#ifdef SK_SUPPORT_LEGACY_LAYER_BITMAP_IMAGEFILTERS
2191 return false;
2192#endif
2193
2194 if (!paint.getImageFilter()) {
2195 return false;
2196 }
2197
2198 const SkMatrix& ctm = this->getTotalMatrix();
2199 const unsigned kSubpixelBits = 0; // matching SkDraw::drawBitmap()
2200 if (!SkTreatAsSprite(ctm, w, h, kSubpixelBits)) {
2201 return false;
2202 }
2203
2204 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2205 // Once we can filter and the filter will return a result larger than itself, we should be
2206 // able to remove this constraint.
2207 // skbug.com/4526
2208 //
2209 SkPoint pt;
2210 ctm.mapXY(x, y, &pt);
2211 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2212 return ir.contains(fMCRec->fRasterClip.getBounds());
2213}
2214
reeda85d4d02015-05-06 12:56:48 -07002215void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002216 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002217 SkRect bounds = SkRect::MakeXYWH(x, y,
2218 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002219 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002220 SkRect tmp = bounds;
2221 if (paint) {
2222 paint->computeFastBounds(tmp, &tmp);
2223 }
2224 if (this->quickReject(tmp)) {
2225 return;
2226 }
reeda85d4d02015-05-06 12:56:48 -07002227 }
2228
2229 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002230 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002231 paint = lazy.init();
2232 }
reed262a71b2015-12-05 13:07:27 -08002233
2234 const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2235 *paint);
2236 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2237
reeda85d4d02015-05-06 12:56:48 -07002238 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002239 const SkPaint& pnt = looper.paint();
2240 if (drawAsSprite && pnt.getImageFilter()) {
2241 SkBitmap bitmap;
2242 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2243 SkPoint pt;
2244 iter.fMatrix->mapXY(x, y, &pt);
2245 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2246 SkScalarRoundToInt(pt.fX),
2247 SkScalarRoundToInt(pt.fY), pnt);
2248 }
2249 } else {
2250 iter.fDevice->drawImage(iter, image, x, y, pnt);
2251 }
reeda85d4d02015-05-06 12:56:48 -07002252 }
2253
2254 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002255}
2256
reed41af9662015-01-05 07:49:08 -08002257void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002258 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002259 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002260 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002261 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002262 if (paint) {
2263 paint->computeFastBounds(dst, &storage);
2264 }
2265 if (this->quickReject(storage)) {
2266 return;
2267 }
reeda85d4d02015-05-06 12:56:48 -07002268 }
2269 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002270 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002271 paint = lazy.init();
2272 }
2273
senorblancoc41e7e12015-12-07 12:51:30 -08002274 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002275 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002276
2277 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002278 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002279 }
2280
2281 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002282}
2283
reed41af9662015-01-05 07:49:08 -08002284void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002285 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 SkDEBUGCODE(bitmap.validate();)
2287
reed33366972015-10-08 09:22:02 -07002288 if (bitmap.drawsNothing()) {
2289 return;
2290 }
2291
2292 SkLazyPaint lazy;
2293 if (nullptr == paint) {
2294 paint = lazy.init();
2295 }
2296
2297 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2298
2299 SkRect storage;
2300 const SkRect* bounds = nullptr;
2301 if (paint->canComputeFastBounds()) {
2302 bitmap.getBounds(&storage);
2303 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002304 SkRect tmp = storage;
2305 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2306 return;
2307 }
2308 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 }
reed@google.com4b226022011-01-11 18:32:13 +00002310
reed262a71b2015-12-05 13:07:27 -08002311 const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2312 bitmap.height(), *paint);
2313 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002314
2315 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002316 const SkPaint& pnt = looper.paint();
2317 if (drawAsSprite && pnt.getImageFilter()) {
2318 SkPoint pt;
2319 iter.fMatrix->mapXY(x, y, &pt);
2320 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2321 SkScalarRoundToInt(pt.fX),
2322 SkScalarRoundToInt(pt.fY), pnt);
2323 } else {
2324 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2325 }
reed33366972015-10-08 09:22:02 -07002326 }
reed262a71b2015-12-05 13:07:27 -08002327
reed33366972015-10-08 09:22:02 -07002328 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329}
2330
reed@google.com9987ec32011-09-07 11:57:52 +00002331// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002332void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002333 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002334 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002335 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 return;
2337 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002338
halcanary96fcdcc2015-08-27 07:41:13 -07002339 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002340 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002341 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2342 return;
2343 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 }
reed@google.com3d608122011-11-21 15:16:16 +00002345
reed@google.com33535f32012-09-25 15:37:50 +00002346 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002347 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002348 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002349 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002350
senorblancoc41e7e12015-12-07 12:51:30 -08002351 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002352 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002353
reed@google.com33535f32012-09-25 15:37:50 +00002354 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002355 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002356 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002357
reed@google.com33535f32012-09-25 15:37:50 +00002358 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359}
2360
reed41af9662015-01-05 07:49:08 -08002361void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002362 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002363 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002364 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002365 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002366}
2367
reed4c21dc52015-06-25 12:32:03 -07002368void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2369 const SkPaint* paint) {
2370 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2371
halcanary96fcdcc2015-08-27 07:41:13 -07002372 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002373 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002374 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2375 return;
2376 }
reed@google.com3d608122011-11-21 15:16:16 +00002377 }
reed4c21dc52015-06-25 12:32:03 -07002378
2379 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002380 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002381 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002382 }
reed4c21dc52015-06-25 12:32:03 -07002383
senorblancoc41e7e12015-12-07 12:51:30 -08002384 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002385
2386 while (iter.next()) {
2387 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002388 }
reed4c21dc52015-06-25 12:32:03 -07002389
2390 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002391}
2392
reed41af9662015-01-05 07:49:08 -08002393void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2394 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002395 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002396 SkDEBUGCODE(bitmap.validate();)
2397
halcanary96fcdcc2015-08-27 07:41:13 -07002398 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002399 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002400 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2401 return;
2402 }
reed4c21dc52015-06-25 12:32:03 -07002403 }
2404
2405 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002406 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002407 paint = lazy.init();
2408 }
2409
senorblancoc41e7e12015-12-07 12:51:30 -08002410 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002411
2412 while (iter.next()) {
2413 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2414 }
2415
2416 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002417}
2418
reed@google.comf67e4cf2011-03-15 20:56:58 +00002419class SkDeviceFilteredPaint {
2420public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002421 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002422 uint32_t filteredFlags = device->filterTextFlags(paint);
2423 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002424 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002425 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002426 fPaint = newPaint;
2427 } else {
2428 fPaint = &paint;
2429 }
2430 }
2431
reed@google.comf67e4cf2011-03-15 20:56:58 +00002432 const SkPaint& paint() const { return *fPaint; }
2433
2434private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002435 const SkPaint* fPaint;
2436 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002437};
2438
bungeman@google.com52c748b2011-08-22 21:30:43 +00002439void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2440 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002441 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002442 draw.fDevice->drawRect(draw, r, paint);
2443 } else {
2444 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002445 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002446 draw.fDevice->drawRect(draw, r, p);
2447 }
2448}
2449
2450void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2451 const char text[], size_t byteLength,
2452 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002453 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002454
2455 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002456 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002457 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002458 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002459 return;
2460 }
2461
2462 SkScalar width = 0;
2463 SkPoint start;
2464
2465 start.set(0, 0); // to avoid warning
2466 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2467 SkPaint::kStrikeThruText_Flag)) {
2468 width = paint.measureText(text, byteLength);
2469
2470 SkScalar offsetX = 0;
2471 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2472 offsetX = SkScalarHalf(width);
2473 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2474 offsetX = width;
2475 }
2476 start.set(x - offsetX, y);
2477 }
2478
2479 if (0 == width) {
2480 return;
2481 }
2482
2483 uint32_t flags = paint.getFlags();
2484
2485 if (flags & (SkPaint::kUnderlineText_Flag |
2486 SkPaint::kStrikeThruText_Flag)) {
2487 SkScalar textSize = paint.getTextSize();
2488 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2489 SkRect r;
2490
2491 r.fLeft = start.fX;
2492 r.fRight = start.fX + width;
2493
2494 if (flags & SkPaint::kUnderlineText_Flag) {
2495 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2496 start.fY);
2497 r.fTop = offset;
2498 r.fBottom = offset + height;
2499 DrawRect(draw, paint, r, textSize);
2500 }
2501 if (flags & SkPaint::kStrikeThruText_Flag) {
2502 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2503 start.fY);
2504 r.fTop = offset;
2505 r.fBottom = offset + height;
2506 DrawRect(draw, paint, r, textSize);
2507 }
2508 }
2509}
2510
reed@google.come0d9ce82014-04-23 04:00:17 +00002511void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2512 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002513 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002514
2515 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002516 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002517 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002518 DrawTextDecorations(iter, dfp.paint(),
2519 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002520 }
2521
reed@google.com4e2b3d32011-04-07 14:18:59 +00002522 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002523}
2524
reed@google.come0d9ce82014-04-23 04:00:17 +00002525void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2526 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002527 SkPoint textOffset = SkPoint::Make(0, 0);
2528
halcanary96fcdcc2015-08-27 07:41:13 -07002529 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002530
reed@android.com8a1c16f2008-12-17 15:59:43 +00002531 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002532 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002533 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002534 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002536
reed@google.com4e2b3d32011-04-07 14:18:59 +00002537 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002538}
2539
reed@google.come0d9ce82014-04-23 04:00:17 +00002540void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2541 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002542
2543 SkPoint textOffset = SkPoint::Make(0, constY);
2544
halcanary96fcdcc2015-08-27 07:41:13 -07002545 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002546
reed@android.com8a1c16f2008-12-17 15:59:43 +00002547 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002548 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002549 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002550 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002551 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002552
reed@google.com4e2b3d32011-04-07 14:18:59 +00002553 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002554}
2555
reed@google.come0d9ce82014-04-23 04:00:17 +00002556void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2557 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002558 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002559
reed@android.com8a1c16f2008-12-17 15:59:43 +00002560 while (iter.next()) {
2561 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002562 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002563 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002564
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002565 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002566}
2567
fmalita00d5c2c2014-08-21 08:53:26 -07002568void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2569 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002570
fmalita85d5eb92015-03-04 11:20:12 -08002571 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002572 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002573 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002574 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002575 SkRect tmp;
2576 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2577 return;
2578 }
2579 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002580 }
2581
fmalita024f9962015-03-03 19:08:17 -08002582 // We cannot filter in the looper as we normally do, because the paint is
2583 // incomplete at this point (text-related attributes are embedded within blob run paints).
2584 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002585 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002586
fmalita85d5eb92015-03-04 11:20:12 -08002587 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002588
fmalitaaa1b9122014-08-28 14:32:24 -07002589 while (iter.next()) {
2590 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002591 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002592 }
2593
fmalitaaa1b9122014-08-28 14:32:24 -07002594 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002595
2596 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002597}
2598
reed@google.come0d9ce82014-04-23 04:00:17 +00002599// These will become non-virtual, so they always call the (virtual) onDraw... method
2600void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2601 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002602 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002603 this->onDrawText(text, byteLength, x, y, paint);
2604}
2605void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2606 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002607 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002608 this->onDrawPosText(text, byteLength, pos, paint);
2609}
2610void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2611 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002612 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002613 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2614}
2615void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2616 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002617 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002618 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2619}
fmalita00d5c2c2014-08-21 08:53:26 -07002620void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2621 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002622 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002623 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002624 this->onDrawTextBlob(blob, x, y, paint);
2625 }
2626}
reed@google.come0d9ce82014-04-23 04:00:17 +00002627
reed41af9662015-01-05 07:49:08 -08002628void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2629 const SkPoint verts[], const SkPoint texs[],
2630 const SkColor colors[], SkXfermode* xmode,
2631 const uint16_t indices[], int indexCount,
2632 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002633 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002634 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002635
reed@android.com8a1c16f2008-12-17 15:59:43 +00002636 while (iter.next()) {
2637 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002638 colors, xmode, indices, indexCount,
2639 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002640 }
reed@google.com4b226022011-01-11 18:32:13 +00002641
reed@google.com4e2b3d32011-04-07 14:18:59 +00002642 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002643}
2644
dandovb3c9d1c2014-08-12 08:34:29 -07002645void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2646 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002647 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002648 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002649 return;
2650 }
mtklein6cfa73a2014-08-13 13:33:49 -07002651
dandovecfff212014-08-04 10:02:00 -07002652 // Since a patch is always within the convex hull of the control points, we discard it when its
2653 // bounding rectangle is completely outside the current clip.
2654 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002655 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002656 if (this->quickReject(bounds)) {
2657 return;
2658 }
mtklein6cfa73a2014-08-13 13:33:49 -07002659
dandovb3c9d1c2014-08-12 08:34:29 -07002660 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2661}
2662
2663void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2664 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2665
halcanary96fcdcc2015-08-27 07:41:13 -07002666 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002667
dandovecfff212014-08-04 10:02:00 -07002668 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002669 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002670 }
mtklein6cfa73a2014-08-13 13:33:49 -07002671
dandovecfff212014-08-04 10:02:00 -07002672 LOOPER_END
2673}
2674
reeda8db7282015-07-07 10:22:31 -07002675void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2676 if (dr) {
2677 if (x || y) {
2678 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2679 this->onDrawDrawable(dr, &matrix);
2680 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002681 this->onDrawDrawable(dr, nullptr);
reeda8db7282015-07-07 10:22:31 -07002682 }
reed6a070dc2014-11-11 19:36:09 -08002683 }
2684}
2685
reeda8db7282015-07-07 10:22:31 -07002686void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2687 if (dr) {
2688 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002689 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002690 }
2691 this->onDrawDrawable(dr, matrix);
2692 }
2693}
2694
2695void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2696 SkRect bounds = dr->getBounds();
2697 if (matrix) {
2698 matrix->mapRect(&bounds);
2699 }
2700 if (this->quickReject(bounds)) {
2701 return;
2702 }
2703 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002704}
2705
reed71c3c762015-06-24 10:29:17 -07002706void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2707 const SkColor colors[], int count, SkXfermode::Mode mode,
2708 const SkRect* cull, const SkPaint* paint) {
2709 if (cull && this->quickReject(*cull)) {
2710 return;
2711 }
2712
2713 SkPaint pnt;
2714 if (paint) {
2715 pnt = *paint;
2716 }
2717
halcanary96fcdcc2015-08-27 07:41:13 -07002718 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002719 while (iter.next()) {
2720 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2721 }
2722 LOOPER_END
2723}
2724
reed@android.com8a1c16f2008-12-17 15:59:43 +00002725//////////////////////////////////////////////////////////////////////////////
2726// These methods are NOT virtual, and therefore must call back into virtual
2727// methods, rather than actually drawing themselves.
2728//////////////////////////////////////////////////////////////////////////////
2729
2730void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002731 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002732 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002733 SkPaint paint;
2734
2735 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002736 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002737 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002738 }
2739 this->drawPaint(paint);
2740}
2741
reed@android.com845fdac2009-06-23 03:01:32 +00002742void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002743 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002744 SkPaint paint;
2745
2746 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002747 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002748 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002749 }
2750 this->drawPaint(paint);
2751}
2752
2753void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002754 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002755 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002756
reed@android.com8a1c16f2008-12-17 15:59:43 +00002757 pt.set(x, y);
2758 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2759}
2760
2761void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002762 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002763 SkPoint pt;
2764 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002765
reed@android.com8a1c16f2008-12-17 15:59:43 +00002766 pt.set(x, y);
2767 paint.setColor(color);
2768 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2769}
2770
2771void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2772 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002773 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002774 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002775
reed@android.com8a1c16f2008-12-17 15:59:43 +00002776 pts[0].set(x0, y0);
2777 pts[1].set(x1, y1);
2778 this->drawPoints(kLines_PointMode, 2, pts, paint);
2779}
2780
2781void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2782 SkScalar right, SkScalar bottom,
2783 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002784 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002785 SkRect r;
2786
2787 r.set(left, top, right, bottom);
2788 this->drawRect(r, paint);
2789}
2790
2791void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2792 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002793 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002794 if (radius < 0) {
2795 radius = 0;
2796 }
2797
2798 SkRect r;
2799 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002800 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002801}
2802
2803void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2804 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002805 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002806 if (rx > 0 && ry > 0) {
2807 if (paint.canComputeFastBounds()) {
2808 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002809 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002810 return;
2811 }
2812 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002813 SkRRect rrect;
2814 rrect.setRectXY(r, rx, ry);
2815 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002816 } else {
2817 this->drawRect(r, paint);
2818 }
2819}
2820
reed@android.com8a1c16f2008-12-17 15:59:43 +00002821void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2822 SkScalar sweepAngle, bool useCenter,
2823 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002824 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002825 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2826 this->drawOval(oval, paint);
2827 } else {
2828 SkPath path;
2829 if (useCenter) {
2830 path.moveTo(oval.centerX(), oval.centerY());
2831 }
2832 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2833 if (useCenter) {
2834 path.close();
2835 }
2836 this->drawPath(path, paint);
2837 }
2838}
2839
2840void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2841 const SkPath& path, SkScalar hOffset,
2842 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002843 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002844 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002845
reed@android.com8a1c16f2008-12-17 15:59:43 +00002846 matrix.setTranslate(hOffset, vOffset);
2847 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2848}
2849
reed@android.comf76bacf2009-05-13 14:00:33 +00002850///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002851
2852/**
2853 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2854 * against the playback cost of recursing into the subpicture to get at its actual ops.
2855 *
2856 * For now we pick a conservatively small value, though measurement (and other heuristics like
2857 * the type of ops contained) may justify changing this value.
2858 */
2859#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002860
reedd5fa1a42014-08-09 11:08:05 -07002861void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002862 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002863 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002864 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002865 matrix = nullptr;
reedd5fa1a42014-08-09 11:08:05 -07002866 }
reed1c2c4412015-04-30 13:09:24 -07002867 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2868 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2869 picture->playback(this);
2870 } else {
2871 this->onDrawPicture(picture, matrix, paint);
2872 }
reedd5fa1a42014-08-09 11:08:05 -07002873 }
2874}
robertphillips9b14f262014-06-04 05:40:44 -07002875
reedd5fa1a42014-08-09 11:08:05 -07002876void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2877 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002878 if (!paint || paint->canComputeFastBounds()) {
2879 SkRect bounds = picture->cullRect();
2880 if (paint) {
2881 paint->computeFastBounds(bounds, &bounds);
2882 }
2883 if (matrix) {
2884 matrix->mapRect(&bounds);
2885 }
2886 if (this->quickReject(bounds)) {
2887 return;
2888 }
2889 }
2890
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002891 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002892 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002893 // Canvas has to first give the device the opportunity to render
2894 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002895 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002896 return; // the device has rendered the entire picture
2897 }
2898 }
2899
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002900 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002901 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002902}
2903
reed@android.com8a1c16f2008-12-17 15:59:43 +00002904///////////////////////////////////////////////////////////////////////////////
2905///////////////////////////////////////////////////////////////////////////////
2906
2907SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002908 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002909
2910 SkASSERT(canvas);
2911
2912 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2913 fDone = !fImpl->next();
2914}
2915
2916SkCanvas::LayerIter::~LayerIter() {
2917 fImpl->~SkDrawIter();
2918}
2919
2920void SkCanvas::LayerIter::next() {
2921 fDone = !fImpl->next();
2922}
2923
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002924SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002925 return fImpl->getDevice();
2926}
2927
2928const SkMatrix& SkCanvas::LayerIter::matrix() const {
2929 return fImpl->getMatrix();
2930}
2931
2932const SkPaint& SkCanvas::LayerIter::paint() const {
2933 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002934 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935 paint = &fDefaultPaint;
2936 }
2937 return *paint;
2938}
2939
2940const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2941int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2942int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002943
2944///////////////////////////////////////////////////////////////////////////////
2945
fmalitac3b589a2014-06-05 12:40:07 -07002946SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002947
2948///////////////////////////////////////////////////////////////////////////////
2949
2950static bool supported_for_raster_canvas(const SkImageInfo& info) {
2951 switch (info.alphaType()) {
2952 case kPremul_SkAlphaType:
2953 case kOpaque_SkAlphaType:
2954 break;
2955 default:
2956 return false;
2957 }
2958
2959 switch (info.colorType()) {
2960 case kAlpha_8_SkColorType:
2961 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002962 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002963 break;
2964 default:
2965 return false;
2966 }
2967
2968 return true;
2969}
2970
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002971SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2972 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002973 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002974 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002975
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002976 SkBitmap bitmap;
2977 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002978 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002979 }
halcanary385fe4d2015-08-26 13:07:48 -07002980 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002981}
reedd5fa1a42014-08-09 11:08:05 -07002982
2983///////////////////////////////////////////////////////////////////////////////
2984
2985SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002986 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002987 : fCanvas(canvas)
2988 , fSaveCount(canvas->getSaveCount())
2989{
bsalomon49f085d2014-09-05 13:34:00 -07002990 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002991 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002992 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002993 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002994 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002995 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002996 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002997 canvas->save();
2998 }
mtklein6cfa73a2014-08-13 13:33:49 -07002999
bsalomon49f085d2014-09-05 13:34:00 -07003000 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003001 canvas->concat(*matrix);
3002 }
3003}
3004
3005SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3006 fCanvas->restoreToCount(fSaveCount);
3007}