blob: 88dddf412a7ae663c1e34d343a8ddd953f8ca1dc [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()");
reeda85d4d02015-05-06 12:56:48 -07002260 SkRect storage;
2261 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002262 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002263 storage = dst;
2264 if (paint) {
2265 paint->computeFastBounds(dst, &storage);
2266 }
2267 if (this->quickReject(storage)) {
2268 return;
2269 }
reeda85d4d02015-05-06 12:56:48 -07002270 }
2271 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002272 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002273 paint = lazy.init();
2274 }
2275
reedc83a2972015-07-16 07:40:45 -07002276 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2277 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002278
2279 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002280 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002281 }
2282
2283 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002284}
2285
reed41af9662015-01-05 07:49:08 -08002286void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002287 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288 SkDEBUGCODE(bitmap.validate();)
2289
reed33366972015-10-08 09:22:02 -07002290 if (bitmap.drawsNothing()) {
2291 return;
2292 }
2293
2294 SkLazyPaint lazy;
2295 if (nullptr == paint) {
2296 paint = lazy.init();
2297 }
2298
2299 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2300
2301 SkRect storage;
2302 const SkRect* bounds = nullptr;
2303 if (paint->canComputeFastBounds()) {
2304 bitmap.getBounds(&storage);
2305 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002306 SkRect tmp = storage;
2307 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2308 return;
2309 }
2310 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 }
reed@google.com4b226022011-01-11 18:32:13 +00002312
reed262a71b2015-12-05 13:07:27 -08002313 const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2314 bitmap.height(), *paint);
2315 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002316
2317 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002318 const SkPaint& pnt = looper.paint();
2319 if (drawAsSprite && pnt.getImageFilter()) {
2320 SkPoint pt;
2321 iter.fMatrix->mapXY(x, y, &pt);
2322 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2323 SkScalarRoundToInt(pt.fX),
2324 SkScalarRoundToInt(pt.fY), pnt);
2325 } else {
2326 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2327 }
reed33366972015-10-08 09:22:02 -07002328 }
reed262a71b2015-12-05 13:07:27 -08002329
reed33366972015-10-08 09:22:02 -07002330 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002331}
2332
reed@google.com9987ec32011-09-07 11:57:52 +00002333// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002334void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002335 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002336 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002337 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338 return;
2339 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002340
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002341 SkRect storage;
2342 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002343 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002344 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2345 return;
2346 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002347 }
reed@google.com3d608122011-11-21 15:16:16 +00002348
reed@google.com33535f32012-09-25 15:37:50 +00002349 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002350 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002351 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002353
reedc83a2972015-07-16 07:40:45 -07002354 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2355 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002356
reed@google.com33535f32012-09-25 15:37:50 +00002357 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002358 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002359 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002360
reed@google.com33535f32012-09-25 15:37:50 +00002361 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362}
2363
reed41af9662015-01-05 07:49:08 -08002364void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002365 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002366 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002367 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002368 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002369}
2370
reed4c21dc52015-06-25 12:32:03 -07002371void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2372 const SkPaint* paint) {
2373 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2374
2375 SkRect storage;
2376 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002377 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002378 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2379 return;
2380 }
reed@google.com3d608122011-11-21 15:16:16 +00002381 }
reed4c21dc52015-06-25 12:32:03 -07002382
2383 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002384 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002385 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002386 }
reed4c21dc52015-06-25 12:32:03 -07002387
2388 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2389
2390 while (iter.next()) {
2391 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002392 }
reed4c21dc52015-06-25 12:32:03 -07002393
2394 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002395}
2396
reed41af9662015-01-05 07:49:08 -08002397void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2398 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002399 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002400 SkDEBUGCODE(bitmap.validate();)
2401
reed4c21dc52015-06-25 12:32:03 -07002402 SkRect storage;
2403 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002404 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002405 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2406 return;
2407 }
reed4c21dc52015-06-25 12:32:03 -07002408 }
2409
2410 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002411 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002412 paint = lazy.init();
2413 }
2414
2415 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2416
2417 while (iter.next()) {
2418 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2419 }
2420
2421 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002422}
2423
reed@google.comf67e4cf2011-03-15 20:56:58 +00002424class SkDeviceFilteredPaint {
2425public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002426 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002427 uint32_t filteredFlags = device->filterTextFlags(paint);
2428 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002429 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002430 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002431 fPaint = newPaint;
2432 } else {
2433 fPaint = &paint;
2434 }
2435 }
2436
reed@google.comf67e4cf2011-03-15 20:56:58 +00002437 const SkPaint& paint() const { return *fPaint; }
2438
2439private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002440 const SkPaint* fPaint;
2441 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002442};
2443
bungeman@google.com52c748b2011-08-22 21:30:43 +00002444void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2445 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002446 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002447 draw.fDevice->drawRect(draw, r, paint);
2448 } else {
2449 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002450 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002451 draw.fDevice->drawRect(draw, r, p);
2452 }
2453}
2454
2455void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2456 const char text[], size_t byteLength,
2457 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002458 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002459
2460 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002461 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002462 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002463 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002464 return;
2465 }
2466
2467 SkScalar width = 0;
2468 SkPoint start;
2469
2470 start.set(0, 0); // to avoid warning
2471 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2472 SkPaint::kStrikeThruText_Flag)) {
2473 width = paint.measureText(text, byteLength);
2474
2475 SkScalar offsetX = 0;
2476 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2477 offsetX = SkScalarHalf(width);
2478 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2479 offsetX = width;
2480 }
2481 start.set(x - offsetX, y);
2482 }
2483
2484 if (0 == width) {
2485 return;
2486 }
2487
2488 uint32_t flags = paint.getFlags();
2489
2490 if (flags & (SkPaint::kUnderlineText_Flag |
2491 SkPaint::kStrikeThruText_Flag)) {
2492 SkScalar textSize = paint.getTextSize();
2493 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2494 SkRect r;
2495
2496 r.fLeft = start.fX;
2497 r.fRight = start.fX + width;
2498
2499 if (flags & SkPaint::kUnderlineText_Flag) {
2500 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2501 start.fY);
2502 r.fTop = offset;
2503 r.fBottom = offset + height;
2504 DrawRect(draw, paint, r, textSize);
2505 }
2506 if (flags & SkPaint::kStrikeThruText_Flag) {
2507 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2508 start.fY);
2509 r.fTop = offset;
2510 r.fBottom = offset + height;
2511 DrawRect(draw, paint, r, textSize);
2512 }
2513 }
2514}
2515
reed@google.come0d9ce82014-04-23 04:00:17 +00002516void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2517 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002518 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002519
2520 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002521 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002522 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002523 DrawTextDecorations(iter, dfp.paint(),
2524 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002525 }
2526
reed@google.com4e2b3d32011-04-07 14:18:59 +00002527 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002528}
2529
reed@google.come0d9ce82014-04-23 04:00:17 +00002530void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2531 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002532 SkPoint textOffset = SkPoint::Make(0, 0);
2533
halcanary96fcdcc2015-08-27 07:41:13 -07002534 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002535
reed@android.com8a1c16f2008-12-17 15:59:43 +00002536 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002537 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002538 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002539 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002540 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002541
reed@google.com4e2b3d32011-04-07 14:18:59 +00002542 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002543}
2544
reed@google.come0d9ce82014-04-23 04:00:17 +00002545void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2546 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002547
2548 SkPoint textOffset = SkPoint::Make(0, constY);
2549
halcanary96fcdcc2015-08-27 07:41:13 -07002550 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002551
reed@android.com8a1c16f2008-12-17 15:59:43 +00002552 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002553 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002554 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002555 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002556 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002557
reed@google.com4e2b3d32011-04-07 14:18:59 +00002558 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002559}
2560
reed@google.come0d9ce82014-04-23 04:00:17 +00002561void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2562 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002563 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002564
reed@android.com8a1c16f2008-12-17 15:59:43 +00002565 while (iter.next()) {
2566 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002567 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002569
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002570 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002571}
2572
fmalita00d5c2c2014-08-21 08:53:26 -07002573void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2574 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002575
fmalita85d5eb92015-03-04 11:20:12 -08002576 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002577 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002578 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002579 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002580 SkRect tmp;
2581 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2582 return;
2583 }
2584 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002585 }
2586
fmalita024f9962015-03-03 19:08:17 -08002587 // We cannot filter in the looper as we normally do, because the paint is
2588 // incomplete at this point (text-related attributes are embedded within blob run paints).
2589 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002590 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002591
fmalita85d5eb92015-03-04 11:20:12 -08002592 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002593
fmalitaaa1b9122014-08-28 14:32:24 -07002594 while (iter.next()) {
2595 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002596 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002597 }
2598
fmalitaaa1b9122014-08-28 14:32:24 -07002599 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002600
2601 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002602}
2603
reed@google.come0d9ce82014-04-23 04:00:17 +00002604// These will become non-virtual, so they always call the (virtual) onDraw... method
2605void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2606 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002607 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002608 this->onDrawText(text, byteLength, x, y, paint);
2609}
2610void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2611 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002612 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002613 this->onDrawPosText(text, byteLength, pos, paint);
2614}
2615void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2616 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002617 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002618 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2619}
2620void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2621 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002622 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002623 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2624}
fmalita00d5c2c2014-08-21 08:53:26 -07002625void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2626 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002627 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002628 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002629 this->onDrawTextBlob(blob, x, y, paint);
2630 }
2631}
reed@google.come0d9ce82014-04-23 04:00:17 +00002632
reed41af9662015-01-05 07:49:08 -08002633void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2634 const SkPoint verts[], const SkPoint texs[],
2635 const SkColor colors[], SkXfermode* xmode,
2636 const uint16_t indices[], int indexCount,
2637 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002638 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002639 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002640
reed@android.com8a1c16f2008-12-17 15:59:43 +00002641 while (iter.next()) {
2642 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002643 colors, xmode, indices, indexCount,
2644 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002645 }
reed@google.com4b226022011-01-11 18:32:13 +00002646
reed@google.com4e2b3d32011-04-07 14:18:59 +00002647 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002648}
2649
dandovb3c9d1c2014-08-12 08:34:29 -07002650void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2651 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002652 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002653 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002654 return;
2655 }
mtklein6cfa73a2014-08-13 13:33:49 -07002656
dandovecfff212014-08-04 10:02:00 -07002657 // Since a patch is always within the convex hull of the control points, we discard it when its
2658 // bounding rectangle is completely outside the current clip.
2659 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002660 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002661 if (this->quickReject(bounds)) {
2662 return;
2663 }
mtklein6cfa73a2014-08-13 13:33:49 -07002664
dandovb3c9d1c2014-08-12 08:34:29 -07002665 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2666}
2667
2668void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2669 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2670
halcanary96fcdcc2015-08-27 07:41:13 -07002671 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002672
dandovecfff212014-08-04 10:02:00 -07002673 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002674 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002675 }
mtklein6cfa73a2014-08-13 13:33:49 -07002676
dandovecfff212014-08-04 10:02:00 -07002677 LOOPER_END
2678}
2679
reeda8db7282015-07-07 10:22:31 -07002680void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2681 if (dr) {
2682 if (x || y) {
2683 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2684 this->onDrawDrawable(dr, &matrix);
2685 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002686 this->onDrawDrawable(dr, nullptr);
reeda8db7282015-07-07 10:22:31 -07002687 }
reed6a070dc2014-11-11 19:36:09 -08002688 }
2689}
2690
reeda8db7282015-07-07 10:22:31 -07002691void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2692 if (dr) {
2693 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002694 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002695 }
2696 this->onDrawDrawable(dr, matrix);
2697 }
2698}
2699
2700void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2701 SkRect bounds = dr->getBounds();
2702 if (matrix) {
2703 matrix->mapRect(&bounds);
2704 }
2705 if (this->quickReject(bounds)) {
2706 return;
2707 }
2708 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002709}
2710
reed71c3c762015-06-24 10:29:17 -07002711void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2712 const SkColor colors[], int count, SkXfermode::Mode mode,
2713 const SkRect* cull, const SkPaint* paint) {
2714 if (cull && this->quickReject(*cull)) {
2715 return;
2716 }
2717
2718 SkPaint pnt;
2719 if (paint) {
2720 pnt = *paint;
2721 }
2722
halcanary96fcdcc2015-08-27 07:41:13 -07002723 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002724 while (iter.next()) {
2725 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2726 }
2727 LOOPER_END
2728}
2729
reed@android.com8a1c16f2008-12-17 15:59:43 +00002730//////////////////////////////////////////////////////////////////////////////
2731// These methods are NOT virtual, and therefore must call back into virtual
2732// methods, rather than actually drawing themselves.
2733//////////////////////////////////////////////////////////////////////////////
2734
2735void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002736 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002737 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002738 SkPaint paint;
2739
2740 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002741 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002742 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002743 }
2744 this->drawPaint(paint);
2745}
2746
reed@android.com845fdac2009-06-23 03:01:32 +00002747void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002748 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002749 SkPaint paint;
2750
2751 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002752 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002753 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002754 }
2755 this->drawPaint(paint);
2756}
2757
2758void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002759 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002760 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002761
reed@android.com8a1c16f2008-12-17 15:59:43 +00002762 pt.set(x, y);
2763 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2764}
2765
2766void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002767 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002768 SkPoint pt;
2769 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002770
reed@android.com8a1c16f2008-12-17 15:59:43 +00002771 pt.set(x, y);
2772 paint.setColor(color);
2773 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2774}
2775
2776void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2777 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002778 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002779 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002780
reed@android.com8a1c16f2008-12-17 15:59:43 +00002781 pts[0].set(x0, y0);
2782 pts[1].set(x1, y1);
2783 this->drawPoints(kLines_PointMode, 2, pts, paint);
2784}
2785
2786void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2787 SkScalar right, SkScalar bottom,
2788 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002789 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002790 SkRect r;
2791
2792 r.set(left, top, right, bottom);
2793 this->drawRect(r, paint);
2794}
2795
2796void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2797 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002798 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002799 if (radius < 0) {
2800 radius = 0;
2801 }
2802
2803 SkRect r;
2804 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002805 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002806}
2807
2808void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2809 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002810 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002811 if (rx > 0 && ry > 0) {
2812 if (paint.canComputeFastBounds()) {
2813 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002814 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002815 return;
2816 }
2817 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002818 SkRRect rrect;
2819 rrect.setRectXY(r, rx, ry);
2820 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002821 } else {
2822 this->drawRect(r, paint);
2823 }
2824}
2825
reed@android.com8a1c16f2008-12-17 15:59:43 +00002826void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2827 SkScalar sweepAngle, bool useCenter,
2828 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002829 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002830 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2831 this->drawOval(oval, paint);
2832 } else {
2833 SkPath path;
2834 if (useCenter) {
2835 path.moveTo(oval.centerX(), oval.centerY());
2836 }
2837 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2838 if (useCenter) {
2839 path.close();
2840 }
2841 this->drawPath(path, paint);
2842 }
2843}
2844
2845void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2846 const SkPath& path, SkScalar hOffset,
2847 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002848 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002849 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002850
reed@android.com8a1c16f2008-12-17 15:59:43 +00002851 matrix.setTranslate(hOffset, vOffset);
2852 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2853}
2854
reed@android.comf76bacf2009-05-13 14:00:33 +00002855///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002856
2857/**
2858 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2859 * against the playback cost of recursing into the subpicture to get at its actual ops.
2860 *
2861 * For now we pick a conservatively small value, though measurement (and other heuristics like
2862 * the type of ops contained) may justify changing this value.
2863 */
2864#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002865
reedd5fa1a42014-08-09 11:08:05 -07002866void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002867 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002868 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002869 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002870 matrix = nullptr;
reedd5fa1a42014-08-09 11:08:05 -07002871 }
reed1c2c4412015-04-30 13:09:24 -07002872 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2873 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2874 picture->playback(this);
2875 } else {
2876 this->onDrawPicture(picture, matrix, paint);
2877 }
reedd5fa1a42014-08-09 11:08:05 -07002878 }
2879}
robertphillips9b14f262014-06-04 05:40:44 -07002880
reedd5fa1a42014-08-09 11:08:05 -07002881void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2882 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002883 if (!paint || paint->canComputeFastBounds()) {
2884 SkRect bounds = picture->cullRect();
2885 if (paint) {
2886 paint->computeFastBounds(bounds, &bounds);
2887 }
2888 if (matrix) {
2889 matrix->mapRect(&bounds);
2890 }
2891 if (this->quickReject(bounds)) {
2892 return;
2893 }
2894 }
2895
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002896 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002897 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002898 // Canvas has to first give the device the opportunity to render
2899 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002900 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002901 return; // the device has rendered the entire picture
2902 }
2903 }
2904
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002905 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002906 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002907}
2908
reed@android.com8a1c16f2008-12-17 15:59:43 +00002909///////////////////////////////////////////////////////////////////////////////
2910///////////////////////////////////////////////////////////////////////////////
2911
2912SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002913 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002914
2915 SkASSERT(canvas);
2916
2917 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2918 fDone = !fImpl->next();
2919}
2920
2921SkCanvas::LayerIter::~LayerIter() {
2922 fImpl->~SkDrawIter();
2923}
2924
2925void SkCanvas::LayerIter::next() {
2926 fDone = !fImpl->next();
2927}
2928
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002929SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002930 return fImpl->getDevice();
2931}
2932
2933const SkMatrix& SkCanvas::LayerIter::matrix() const {
2934 return fImpl->getMatrix();
2935}
2936
2937const SkPaint& SkCanvas::LayerIter::paint() const {
2938 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002939 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940 paint = &fDefaultPaint;
2941 }
2942 return *paint;
2943}
2944
2945const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2946int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2947int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002948
2949///////////////////////////////////////////////////////////////////////////////
2950
fmalitac3b589a2014-06-05 12:40:07 -07002951SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002952
2953///////////////////////////////////////////////////////////////////////////////
2954
2955static bool supported_for_raster_canvas(const SkImageInfo& info) {
2956 switch (info.alphaType()) {
2957 case kPremul_SkAlphaType:
2958 case kOpaque_SkAlphaType:
2959 break;
2960 default:
2961 return false;
2962 }
2963
2964 switch (info.colorType()) {
2965 case kAlpha_8_SkColorType:
2966 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002967 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002968 break;
2969 default:
2970 return false;
2971 }
2972
2973 return true;
2974}
2975
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002976SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2977 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002978 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002979 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002980
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002981 SkBitmap bitmap;
2982 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002983 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002984 }
halcanary385fe4d2015-08-26 13:07:48 -07002985 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002986}
reedd5fa1a42014-08-09 11:08:05 -07002987
2988///////////////////////////////////////////////////////////////////////////////
2989
2990SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002991 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002992 : fCanvas(canvas)
2993 , fSaveCount(canvas->getSaveCount())
2994{
bsalomon49f085d2014-09-05 13:34:00 -07002995 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002996 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002997 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002998 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002999 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003000 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003001 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003002 canvas->save();
3003 }
mtklein6cfa73a2014-08-13 13:33:49 -07003004
bsalomon49f085d2014-09-05 13:34:00 -07003005 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003006 canvas->concat(*matrix);
3007 }
3008}
3009
3010SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3011 fCanvas->restoreToCount(fSaveCount);
3012}