blob: e039c60e0a4586ce0816ee575adf7a797e3073c0 [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070022#include "SkLatticeIter.h"
reed262a71b2015-12-05 13:07:27 -080023#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000024#include "SkMetaData.h"
msarettfbfa2582016-08-12 08:29:08 -070025#include "SkNx.h"
reedc83a2972015-07-16 07:40:45 -070026#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070027#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000029#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080030#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000031#include "SkRRect.h"
vjiaoblack904527d2016-08-09 09:32:09 -070032#include "SkShadowPaintFilterCanvas.h"
33#include "SkShadowShader.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000034#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080035#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000036#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070037#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000038#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000039#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080040#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070041
42#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000043
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000044#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080045#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000046#include "GrRenderTarget.h"
bsalomon614d8f92016-07-13 15:42:40 -070047#include "SkGrPriv.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000048#endif
49
reede3b38ce2016-01-08 09:18:44 -080050#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
51
reed2d1afab2016-06-29 14:33:11 -070052//#define SK_SUPPORT_PRECHECK_CLIPRECT
53
reedc83a2972015-07-16 07:40:45 -070054/*
55 * Return true if the drawing this rect would hit every pixels in the canvas.
56 *
57 * Returns false if
58 * - rect does not contain the canvas' bounds
59 * - paint is not fill
60 * - paint would blur or otherwise change the coverage of the rect
61 */
62bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
63 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070064 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
65 (int)kNone_ShaderOverrideOpacity,
66 "need_matching_enums0");
67 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
68 (int)kOpaque_ShaderOverrideOpacity,
69 "need_matching_enums1");
70 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
71 (int)kNotOpaque_ShaderOverrideOpacity,
72 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070073
74 const SkISize size = this->getBaseLayerSize();
75 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
76 if (!this->getClipStack()->quickContains(bounds)) {
77 return false;
78 }
79
80 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070081 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070082 return false; // conservative
83 }
halcanaryc5769b22016-08-10 07:13:21 -070084
85 SkRect devRect;
86 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
87 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070088 return false;
89 }
90 }
91
92 if (paint) {
93 SkPaint::Style paintStyle = paint->getStyle();
94 if (!(paintStyle == SkPaint::kFill_Style ||
95 paintStyle == SkPaint::kStrokeAndFill_Style)) {
96 return false;
97 }
98 if (paint->getMaskFilter() || paint->getLooper()
99 || paint->getPathEffect() || paint->getImageFilter()) {
100 return false; // conservative
101 }
102 }
103 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
104}
105
106///////////////////////////////////////////////////////////////////////////////////////////////////
107
reedd990e2f2014-12-22 11:58:30 -0800108static bool gIgnoreSaveLayerBounds;
109void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
110 gIgnoreSaveLayerBounds = ignore;
111}
112bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
113 return gIgnoreSaveLayerBounds;
114}
115
reed0acf1b42014-12-22 16:12:38 -0800116static bool gTreatSpriteAsBitmap;
117void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
118 gTreatSpriteAsBitmap = spriteAsBitmap;
119}
120bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
121 return gTreatSpriteAsBitmap;
122}
123
reed@google.comda17f752012-08-16 18:27:05 +0000124// experimental for faster tiled drawing...
125//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126//#define SK_TRACE_SAVERESTORE
127
128#ifdef SK_TRACE_SAVERESTORE
129 static int gLayerCounter;
130 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
131 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
132
133 static int gRecCounter;
134 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
135 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
136
137 static int gCanvasCounter;
138 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
139 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
140#else
141 #define inc_layer()
142 #define dec_layer()
143 #define inc_rec()
144 #define dec_rec()
145 #define inc_canvas()
146 #define dec_canvas()
147#endif
148
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000149typedef SkTLazy<SkPaint> SkLazyPaint;
150
reedc83a2972015-07-16 07:40:45 -0700151void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000152 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700153 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
154 ? SkSurface::kDiscard_ContentChangeMode
155 : SkSurface::kRetain_ContentChangeMode);
156 }
157}
158
159void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
160 ShaderOverrideOpacity overrideOpacity) {
161 if (fSurfaceBase) {
162 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
163 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
164 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
165 // and therefore we don't care which mode we're in.
166 //
167 if (fSurfaceBase->outstandingImageSnapshot()) {
168 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
169 mode = SkSurface::kDiscard_ContentChangeMode;
170 }
171 }
172 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000173 }
174}
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000178/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 The clip/matrix/proc are fields that reflect the top of the save/restore
180 stack. Whenever the canvas changes, it marks a dirty flag, and then before
181 these are used (assuming we're not on a layer) we rebuild these cache
182 values: they reflect the top of the save stack, but translated and clipped
183 by the device's XY offset and bitmap-bounds.
184*/
185struct DeviceCM {
186 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000187 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000188 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000189 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700190 const SkMatrix* fMatrix;
191 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700192 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
reed96e657d2015-03-10 17:30:07 -0700194 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700195 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700196 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700197 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700198 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700199 {
reed2c9e2002016-07-25 08:05:22 -0700200 SkSafeRef(device);
reed@google.com4b226022011-01-11 18:32:13 +0000201 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700202 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000203 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000205 ~DeviceCM() {
reed2c9e2002016-07-25 08:05:22 -0700206 SkSafeUnref(fDevice);
halcanary385fe4d2015-08-26 13:07:48 -0700207 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000208 }
reed@google.com4b226022011-01-11 18:32:13 +0000209
mtkleinfeaadee2015-04-08 11:25:48 -0700210 void reset(const SkIRect& bounds) {
211 SkASSERT(!fPaint);
212 SkASSERT(!fNext);
213 SkASSERT(fDevice);
214 fClip.setRect(bounds);
215 }
216
reed@google.com045e62d2011-10-24 12:19:46 +0000217 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
218 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000219 int x = fDevice->getOrigin().x();
220 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 int width = fDevice->width();
222 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000223
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 if ((x | y) == 0) {
225 fMatrix = &totalMatrix;
226 fClip = totalClip;
227 } else {
228 fMatrixStorage = totalMatrix;
229 fMatrixStorage.postTranslate(SkIntToScalar(-x),
230 SkIntToScalar(-y));
231 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 totalClip.translate(-x, -y, &fClip);
234 }
235
reed@google.com045e62d2011-10-24 12:19:46 +0000236 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
238 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000241 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 SkRegion::kDifference_Op);
243 }
reed@google.com4b226022011-01-11 18:32:13 +0000244
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000245 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247#ifdef SK_DEBUG
248 if (!fClip.isEmpty()) {
249 SkIRect deviceR;
250 deviceR.set(0, 0, width, height);
251 SkASSERT(deviceR.contains(fClip.getBounds()));
252 }
253#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000254 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255};
256
257/* This is the record we keep for each save/restore level in the stack.
258 Since a level optionally copies the matrix and/or stack, we have pointers
259 for these fields. If the value is copied for this level, the copy is
260 stored in the ...Storage field, and the pointer points to that. If the
261 value is not copied for this level, we ignore ...Storage, and just point
262 at the corresponding value in the previous level in the stack.
263*/
264class SkCanvas::MCRec {
265public:
reed1f836ee2014-07-07 07:49:34 -0700266 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700267 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 /* If there are any layers in the stack, this points to the top-most
269 one that is at or below this level in the stack (so we know what
270 bitmap/device to draw into from this level. This value is NOT
271 reference counted, since the real owner is either our fLayer field,
272 or a previous one in a lower level.)
273 */
reed2ff1fce2014-12-11 07:07:37 -0800274 DeviceCM* fTopLayer;
275 SkRasterClip fRasterClip;
276 SkMatrix fMatrix;
277 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278
vjiaoblacke5de1302016-07-13 14:05:28 -0700279 // This is the current cumulative depth (aggregate of all done translateZ calls)
280 SkScalar fCurDrawDepth;
281
reedd9544982014-09-09 18:46:22 -0700282 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700283 fFilter = nullptr;
284 fLayer = nullptr;
285 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800286 fMatrix.reset();
287 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700288 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700289
reedd9544982014-09-09 18:46:22 -0700290 // don't bother initializing fNext
291 inc_rec();
292 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700293 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
294 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700295 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700296 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700297 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800298 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700299
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 // don't bother initializing fNext
301 inc_rec();
302 }
303 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000304 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700305 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 dec_rec();
307 }
mtkleinfeaadee2015-04-08 11:25:48 -0700308
309 void reset(const SkIRect& bounds) {
310 SkASSERT(fLayer);
311 SkASSERT(fDeferredSaveCount == 0);
312
313 fMatrix.reset();
314 fRasterClip.setRect(bounds);
315 fLayer->reset(bounds);
316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317};
318
319class SkDrawIter : public SkDraw {
320public:
reed3aafe112016-08-18 12:45:34 -0700321 SkDrawIter(SkCanvas* canvas) {
junov@google.com4370aed2012-01-18 16:21:08 +0000322 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000323 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 canvas->updateDeviceCMCache();
325
reed687fa1c2015-04-07 08:00:56 -0700326 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 fCurrLayer = canvas->fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 }
reed@google.com4b226022011-01-11 18:32:13 +0000329
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 bool next() {
331 // skip over recs with empty clips
reed3aafe112016-08-18 12:45:34 -0700332 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
333 fCurrLayer = fCurrLayer->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 }
335
reed@google.comf68c5e22012-02-24 16:38:58 +0000336 const DeviceCM* rec = fCurrLayer;
337 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338
339 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000340 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700342 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700343 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700344 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000346 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
348 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700349 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000350
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 return true;
352 }
353 return false;
354 }
reed@google.com4b226022011-01-11 18:32:13 +0000355
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000356 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700357 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000358 int getX() const { return fDevice->getOrigin().x(); }
359 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363private:
364 SkCanvas* fCanvas;
365 const DeviceCM* fCurrLayer;
366 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367
368 typedef SkDraw INHERITED;
369};
370
371/////////////////////////////////////////////////////////////////////////////
372
reeddbc3cef2015-04-29 12:18:57 -0700373static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
374 return lazy->isValid() ? lazy->get() : lazy->set(orig);
375}
376
377/**
378 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700379 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700380 */
reedd053ce92016-03-22 10:17:23 -0700381static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700382 SkImageFilter* imgf = paint.getImageFilter();
383 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700384 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700385 }
386
reedd053ce92016-03-22 10:17:23 -0700387 SkColorFilter* imgCFPtr;
388 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700389 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700390 }
reedd053ce92016-03-22 10:17:23 -0700391 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700392
393 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700394 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700395 // there is no existing paint colorfilter, so we can just return the imagefilter's
396 return imgCF;
397 }
398
399 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
400 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700401 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700402}
403
senorblanco87e066e2015-10-28 11:23:36 -0700404/**
405 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
406 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
407 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
408 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
409 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
410 * conservative "effective" bounds based on the settings in the paint... with one exception. This
411 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
412 * deliberately ignored.
413 */
414static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
415 const SkRect& rawBounds,
416 SkRect* storage) {
417 SkPaint tmpUnfiltered(paint);
418 tmpUnfiltered.setImageFilter(nullptr);
419 if (tmpUnfiltered.canComputeFastBounds()) {
420 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
421 } else {
422 return rawBounds;
423 }
424}
425
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426class AutoDrawLooper {
427public:
senorblanco87e066e2015-10-28 11:23:36 -0700428 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
429 // paint. It's used to determine the size of the offscreen layer for filters.
430 // If null, the clip will be used instead.
reed3aafe112016-08-18 12:45:34 -0700431 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700432 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800434#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800436#else
437 fFilter = nullptr;
438#endif
reed4a8126e2014-09-22 07:29:03 -0700439 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700441 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000442 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443
reedd053ce92016-03-22 10:17:23 -0700444 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700445 if (simplifiedCF) {
446 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700447 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700448 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700449 fPaint = paint;
450 }
451
452 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700453 /**
454 * We implement ImageFilters for a given draw by creating a layer, then applying the
455 * imagefilter to the pixels of that layer (its backing surface/image), and then
456 * we call restore() to xfer that layer to the main canvas.
457 *
458 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
459 * 2. Generate the src pixels:
460 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
461 * return (fPaint). We then draw the primitive (using srcover) into a cleared
462 * buffer/surface.
463 * 3. Restore the layer created in #1
464 * The imagefilter is passed the buffer/surface from the layer (now filled with the
465 * src pixels of the primitive). It returns a new "filtered" buffer, which we
466 * draw onto the previous layer using the xfermode from the original paint.
467 */
reed@google.com8926b162012-03-23 15:36:36 +0000468 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700469 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700470 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700471 SkRect storage;
472 if (rawBounds) {
473 // Make rawBounds include all paint outsets except for those due to image filters.
474 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
475 }
reedbfd5f172016-01-07 11:28:08 -0800476 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700477 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700478 fTempLayerForImageFilter = true;
479 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000480 }
481
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000482 if (SkDrawLooper* looper = paint.getLooper()) {
483 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
484 looper->contextSize());
485 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000486 fIsSimple = false;
487 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700488 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000489 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700490 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000491 }
492 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000493
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700495 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000496 fCanvas->internalRestore();
497 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000498 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000500
reed@google.com4e2b3d32011-04-07 14:18:59 +0000501 const SkPaint& paint() const {
502 SkASSERT(fPaint);
503 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000505
reed@google.com129ec222012-05-15 13:24:09 +0000506 bool next(SkDrawFilter::Type drawType) {
507 if (fDone) {
508 return false;
509 } else if (fIsSimple) {
510 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000511 return !fPaint->nothingToDraw();
512 } else {
513 return this->doNext(drawType);
514 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517private:
reeddbc3cef2015-04-29 12:18:57 -0700518 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
519 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000520 SkCanvas* fCanvas;
521 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000522 SkDrawFilter* fFilter;
523 const SkPaint* fPaint;
524 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700525 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000526 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000527 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000528 SkDrawLooper::Context* fLooperContext;
529 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000530
531 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532};
533
reed@google.com129ec222012-05-15 13:24:09 +0000534bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700535 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000536 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700537 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000538
reeddbc3cef2015-04-29 12:18:57 -0700539 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
540 *fLazyPaintInit.get() : fOrigPaint);
reed@google.com129ec222012-05-15 13:24:09 +0000541
reed5c476fb2015-04-20 08:04:21 -0700542 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700543 paint->setImageFilter(nullptr);
544 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000545 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000546
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000547 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000548 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000549 return false;
550 }
551 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000552 if (!fFilter->filter(paint, drawType)) {
553 fDone = true;
554 return false;
555 }
halcanary96fcdcc2015-08-27 07:41:13 -0700556 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000557 // no looper means we only draw once
558 fDone = true;
559 }
560 }
561 fPaint = paint;
562
563 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000564 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000565 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000566 }
567
568 // call this after any possible paint modifiers
569 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700570 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571 return false;
572 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000573 return true;
574}
575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576////////// macros to place around the internal draw calls //////////////////
577
reed3aafe112016-08-18 12:45:34 -0700578#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
579 this->predrawNotify(); \
580 AutoDrawLooper looper(this, paint, skipLayerForFilter, bounds); \
581 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
reed262a71b2015-12-05 13:07:27 -0800582 SkDrawIter iter(this);
583
584
reed@google.com8926b162012-03-23 15:36:36 +0000585#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000586 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700587 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000588 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000589 SkDrawIter iter(this);
590
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000591#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000592 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700593 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000594 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000596
reedc83a2972015-07-16 07:40:45 -0700597#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
598 this->predrawNotify(bounds, &paint, auxOpaque); \
reed3aafe112016-08-18 12:45:34 -0700599 AutoDrawLooper looper(this, paint, false, bounds); \
reedc83a2972015-07-16 07:40:45 -0700600 while (looper.next(type)) { \
601 SkDrawIter iter(this);
602
reed@google.com4e2b3d32011-04-07 14:18:59 +0000603#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604
605////////////////////////////////////////////////////////////////////////////
606
msarettfbfa2582016-08-12 08:29:08 -0700607static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
608 if (bounds.isEmpty()) {
609 return SkRect::MakeEmpty();
610 }
611
612 // Expand bounds out by 1 in case we are anti-aliasing. We store the
613 // bounds as floats to enable a faster quick reject implementation.
614 SkRect dst;
615 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
616 return dst;
617}
618
mtkleinfeaadee2015-04-08 11:25:48 -0700619void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
620 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700621 fClipStack->reset();
622 fMCRec->reset(bounds);
623
624 // We're peering through a lot of structs here. Only at this scope do we
625 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
626 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700627 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700628 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700629}
630
reedd9544982014-09-09 18:46:22 -0700631SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800632 if (device && device->forceConservativeRasterClip()) {
633 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
634 }
635 // Since init() is only called once by our constructors, it is safe to perform this
636 // const-cast.
637 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
638
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000639 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000640 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700641 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800642 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700643 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700644#ifdef SK_EXPERIMENTAL_SHADOWING
645 fLights = nullptr;
646#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647
halcanary385fe4d2015-08-26 13:07:48 -0700648 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700651 new (fMCRec) MCRec(fConservativeRasterClip);
msarett9637ea92016-08-18 14:03:30 -0700652 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653
reeda499f902015-05-01 09:34:31 -0700654 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
655 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700656 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700657 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660
halcanary96fcdcc2015-08-27 07:41:13 -0700661 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000662
reedf92c8662014-08-18 08:02:43 -0700663 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700664 // The root device and the canvas should always have the same pixel geometry
665 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700666 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800667 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700668 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700669 }
msarettfbfa2582016-08-12 08:29:08 -0700670
reedf92c8662014-08-18 08:02:43 -0700671 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672}
673
reed@google.comcde92112011-07-06 20:00:52 +0000674SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000675 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700676 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800677 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000678{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000679 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000680
halcanary96fcdcc2015-08-27 07:41:13 -0700681 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000682}
683
reedd9544982014-09-09 18:46:22 -0700684static SkBitmap make_nopixels(int width, int height) {
685 SkBitmap bitmap;
686 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
687 return bitmap;
688}
689
690class SkNoPixelsBitmapDevice : public SkBitmapDevice {
691public:
robertphillipsfcf78292015-06-19 11:49:52 -0700692 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
693 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800694 {
695 this->setOrigin(bounds.x(), bounds.y());
696 }
reedd9544982014-09-09 18:46:22 -0700697
698private:
piotaixrb5fae932014-09-24 13:03:30 -0700699
reedd9544982014-09-09 18:46:22 -0700700 typedef SkBitmapDevice INHERITED;
701};
702
reed96a857e2015-01-25 10:33:58 -0800703SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000704 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800705 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800706 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000707{
708 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700709
halcanary385fe4d2015-08-26 13:07:48 -0700710 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
711 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700712}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000713
reed78e27682014-11-19 08:04:34 -0800714SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700715 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700716 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800717 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700718{
719 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700720
halcanary385fe4d2015-08-26 13:07:48 -0700721 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700722}
723
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000724SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000725 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700726 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800727 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000728{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700730
reedd9544982014-09-09 18:46:22 -0700731 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732}
733
robertphillipsfcf78292015-06-19 11:49:52 -0700734SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
735 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700736 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800737 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700738{
739 inc_canvas();
740
741 this->init(device, flags);
742}
743
reed4a8126e2014-09-22 07:29:03 -0700744SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700745 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700746 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800747 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700748{
749 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700750
halcanary385fe4d2015-08-26 13:07:48 -0700751 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700752 this->init(device, kDefault_InitFlags);
753}
reed29c857d2014-09-21 10:25:07 -0700754
reed4a8126e2014-09-22 07:29:03 -0700755SkCanvas::SkCanvas(const SkBitmap& bitmap)
756 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
757 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800758 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700759{
760 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700761
halcanary385fe4d2015-08-26 13:07:48 -0700762 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700763 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764}
765
766SkCanvas::~SkCanvas() {
767 // free up the contents of our deque
768 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000769
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 this->internalRestore(); // restore the last, since we're going away
771
halcanary385fe4d2015-08-26 13:07:48 -0700772 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000773
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 dec_canvas();
775}
776
fmalita53d9f1c2016-01-25 06:23:54 -0800777#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778SkDrawFilter* SkCanvas::getDrawFilter() const {
779 return fMCRec->fFilter;
780}
781
782SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700783 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
785 return filter;
786}
fmalita77650002016-01-21 18:47:11 -0800787#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000789SkMetaData& SkCanvas::getMetaData() {
790 // metadata users are rare, so we lazily allocate it. If that changes we
791 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700792 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000793 fMetaData = new SkMetaData;
794 }
795 return *fMetaData;
796}
797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798///////////////////////////////////////////////////////////////////////////////
799
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000800void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700801 this->onFlush();
802}
803
804void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000805 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000806 if (device) {
807 device->flush();
808 }
809}
810
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000811SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000812 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000813 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
814}
815
senorblancoafc7cce2016-02-02 18:44:15 -0800816SkIRect SkCanvas::getTopLayerBounds() const {
817 SkBaseDevice* d = this->getTopDevice();
818 if (!d) {
819 return SkIRect::MakeEmpty();
820 }
821 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
822}
823
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000824SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000826 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 SkASSERT(rec && rec->fLayer);
828 return rec->fLayer->fDevice;
829}
830
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000831SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000832 if (updateMatrixClip) {
833 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
834 }
reed@google.com9266fed2011-03-30 00:18:03 +0000835 return fMCRec->fTopLayer->fDevice;
836}
837
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000838bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700839 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000840 return false;
841 }
842
843 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700844 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700845 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000846 return false;
847 }
848 weAllocated = true;
849 }
850
reedcf01e312015-05-23 19:14:51 -0700851 SkAutoPixmapUnlock unlocker;
852 if (bitmap->requestLock(&unlocker)) {
853 const SkPixmap& pm = unlocker.pixmap();
854 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
855 return true;
856 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000857 }
858
859 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700860 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000861 }
862 return false;
863}
reed@google.com51df9e32010-12-23 19:29:18 +0000864
bsalomon@google.comc6980972011-11-02 19:57:21 +0000865bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000866 SkIRect r = srcRect;
867 const SkISize size = this->getBaseLayerSize();
868 if (!r.intersect(0, 0, size.width(), size.height())) {
869 bitmap->reset();
870 return false;
871 }
872
reed84825042014-09-02 12:50:45 -0700873 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000874 // bitmap will already be reset.
875 return false;
876 }
877 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
878 bitmap->reset();
879 return false;
880 }
881 return true;
882}
883
reed96472de2014-12-10 09:53:42 -0800884bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000885 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000886 if (!device) {
887 return false;
888 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000889 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800890
reed96472de2014-12-10 09:53:42 -0800891 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
892 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000893 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000894 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000895
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000896 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800897 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000898}
899
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000900bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700901 SkAutoPixmapUnlock unlocker;
902 if (bitmap.requestLock(&unlocker)) {
903 const SkPixmap& pm = unlocker.pixmap();
904 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000905 }
906 return false;
907}
908
909bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
910 int x, int y) {
911 switch (origInfo.colorType()) {
912 case kUnknown_SkColorType:
913 case kIndex_8_SkColorType:
914 return false;
915 default:
916 break;
917 }
halcanary96fcdcc2015-08-27 07:41:13 -0700918 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000919 return false;
920 }
921
922 const SkISize size = this->getBaseLayerSize();
923 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
924 if (!target.intersect(0, 0, size.width(), size.height())) {
925 return false;
926 }
927
928 SkBaseDevice* device = this->getDevice();
929 if (!device) {
930 return false;
931 }
932
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000933 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700934 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000935
936 // if x or y are negative, then we have to adjust pixels
937 if (x > 0) {
938 x = 0;
939 }
940 if (y > 0) {
941 y = 0;
942 }
943 // here x,y are either 0 or negative
944 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
945
reed4af35f32014-06-27 17:47:49 -0700946 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700947 const bool completeOverwrite = info.dimensions() == size;
948 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700949
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000950 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000951 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000952}
reed@google.com51df9e32010-12-23 19:29:18 +0000953
junov@google.com4370aed2012-01-18 16:21:08 +0000954SkCanvas* SkCanvas::canvasForDrawIter() {
955 return this;
956}
957
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958//////////////////////////////////////////////////////////////////////////////
959
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960void SkCanvas::updateDeviceCMCache() {
961 if (fDeviceCMDirty) {
962 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700963 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000965
halcanary96fcdcc2015-08-27 07:41:13 -0700966 if (nullptr == layer->fNext) { // only one layer
967 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000969 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 do {
reed687fa1c2015-04-07 08:00:56 -0700971 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700972 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 }
974 fDeviceCMDirty = false;
975 }
976}
977
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978///////////////////////////////////////////////////////////////////////////////
979
reed2ff1fce2014-12-11 07:07:37 -0800980void SkCanvas::checkForDeferredSave() {
981 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800982 this->doSave();
983 }
984}
985
reedf0090cb2014-11-26 08:55:51 -0800986int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800987#ifdef SK_DEBUG
988 int count = 0;
989 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
990 for (;;) {
991 const MCRec* rec = (const MCRec*)iter.next();
992 if (!rec) {
993 break;
994 }
995 count += 1 + rec->fDeferredSaveCount;
996 }
997 SkASSERT(count == fSaveCount);
998#endif
999 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001000}
1001
1002int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001003 fSaveCount += 1;
1004 fMCRec->fDeferredSaveCount += 1;
1005 return this->getSaveCount() - 1; // return our prev value
1006}
1007
1008void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001009 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001010
1011 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1012 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001013 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001014}
1015
1016void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001017 if (fMCRec->fDeferredSaveCount > 0) {
1018 SkASSERT(fSaveCount > 1);
1019 fSaveCount -= 1;
1020 fMCRec->fDeferredSaveCount -= 1;
1021 } else {
1022 // check for underflow
1023 if (fMCStack.count() > 1) {
1024 this->willRestore();
1025 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001026 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001027 this->internalRestore();
1028 this->didRestore();
1029 }
reedf0090cb2014-11-26 08:55:51 -08001030 }
1031}
1032
1033void SkCanvas::restoreToCount(int count) {
1034 // sanity check
1035 if (count < 1) {
1036 count = 1;
1037 }
mtkleinf0f14112014-12-12 08:46:25 -08001038
reedf0090cb2014-11-26 08:55:51 -08001039 int n = this->getSaveCount() - count;
1040 for (int i = 0; i < n; ++i) {
1041 this->restore();
1042 }
1043}
1044
reed2ff1fce2014-12-11 07:07:37 -08001045void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001047 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001049
reed687fa1c2015-04-07 08:00:56 -07001050 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051}
1052
reed4960eee2015-12-18 07:09:18 -08001053bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001054#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001055 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001056#else
1057 return true;
1058#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059}
1060
reed4960eee2015-12-18 07:09:18 -08001061bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001062 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001063 SkIRect clipBounds;
1064 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001065 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001066 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001067
reed96e657d2015-03-10 17:30:07 -07001068 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1069
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001070 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001071 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001072 if (bounds && !imageFilter->canComputeFastBounds()) {
1073 bounds = nullptr;
1074 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001075 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001076 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001077 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001079
reed96e657d2015-03-10 17:30:07 -07001080 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 r.roundOut(&ir);
1082 // early exit if the layer's bounds are clipped out
1083 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001084 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001085 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001086 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001087 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001088 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 }
1090 } else { // no user bounds, so just use the clip
1091 ir = clipBounds;
1092 }
reed180aec42015-03-11 10:39:04 -07001093 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094
reed4960eee2015-12-18 07:09:18 -08001095 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001096 // Simplify the current clips since they will be applied properly during restore()
reed687fa1c2015-04-07 08:00:56 -07001097 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001098 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001099 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001100 }
1101
1102 if (intersection) {
1103 *intersection = ir;
1104 }
1105 return true;
1106}
1107
reed4960eee2015-12-18 07:09:18 -08001108
reed4960eee2015-12-18 07:09:18 -08001109int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1110 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001111}
1112
reed70ee31b2015-12-10 13:44:45 -08001113int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001114 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1115}
1116
1117int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1118 SaveLayerRec rec(origRec);
1119 if (gIgnoreSaveLayerBounds) {
1120 rec.fBounds = nullptr;
1121 }
1122 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1123 fSaveCount += 1;
1124 this->internalSaveLayer(rec, strategy);
1125 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001126}
1127
reeda2217ef2016-07-20 06:04:34 -07001128void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1129 SkBaseDevice* dst, const SkMatrix& ctm,
1130 const SkClipStack* clipStack) {
1131 SkDraw draw;
1132 SkRasterClip rc;
1133 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1134 if (!dst->accessPixels(&draw.fDst)) {
1135 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001136 }
reeda2217ef2016-07-20 06:04:34 -07001137 draw.fMatrix = &SkMatrix::I();
1138 draw.fRC = &rc;
1139 draw.fClipStack = clipStack;
1140 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001141
1142 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001143 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001144
1145 int x = src->getOrigin().x() - dst->getOrigin().x();
1146 int y = src->getOrigin().y() - dst->getOrigin().y();
1147 auto special = src->snapSpecial();
1148 if (special) {
1149 dst->drawSpecial(draw, special.get(), x, y, p);
1150 }
robertphillips7354a4b2015-12-16 05:08:27 -08001151}
reed70ee31b2015-12-10 13:44:45 -08001152
reed129ed1c2016-02-22 06:42:31 -08001153static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1154 const SkPaint* paint) {
1155 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1156 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001157 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001158 const bool hasImageFilter = paint && paint->getImageFilter();
1159
1160 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1161 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1162 // force to L32
1163 return SkImageInfo::MakeN32(w, h, alphaType);
1164 } else {
1165 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001166 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001167 }
1168}
1169
reed4960eee2015-12-18 07:09:18 -08001170void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1171 const SkRect* bounds = rec.fBounds;
1172 const SkPaint* paint = rec.fPaint;
1173 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1174
reed@google.comb93ba452014-03-10 19:47:58 +00001175#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001176 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001177#endif
1178
reed8c30a812016-04-20 16:36:51 -07001179 SkLazyPaint lazyP;
1180 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1181 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001182 SkMatrix remainder;
1183 SkSize scale;
1184 /*
1185 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1186 * but they do handle scaling. To accommodate this, we do the following:
1187 *
1188 * 1. Stash off the current CTM
1189 * 2. Decompose the CTM into SCALE and REMAINDER
1190 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1191 * contains the REMAINDER
1192 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1193 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1194 * of the original imagefilter, and draw that (via drawSprite)
1195 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1196 *
1197 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1198 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1199 */
reed96a04f32016-04-25 09:25:15 -07001200 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001201 stashedMatrix.decomposeScale(&scale, &remainder))
1202 {
1203 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1204 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1205 SkPaint* p = lazyP.set(*paint);
1206 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1207 SkFilterQuality::kLow_SkFilterQuality,
1208 sk_ref_sp(imageFilter)));
1209 imageFilter = p->getImageFilter();
1210 paint = p;
1211 }
reed8c30a812016-04-20 16:36:51 -07001212
junov@chromium.orga907ac32012-02-24 21:54:07 +00001213 // do this before we create the layer. We don't call the public save() since
1214 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001215 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001216
1217 fDeviceCMDirty = true;
1218
1219 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001220 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001221 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222 }
1223
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001224 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1225 // the clipRectBounds() call above?
1226 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001227 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001228 }
1229
reed4960eee2015-12-18 07:09:18 -08001230 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001231 SkPixelGeometry geo = fProps.pixelGeometry();
1232 if (paint) {
reed76033be2015-03-14 10:54:31 -07001233 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001234 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001235 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001236 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001237 }
1238 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239
robertphillips5139e502016-07-19 05:10:40 -07001240 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001241 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001242 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001243 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001244 }
reedb2db8982014-11-13 12:41:02 -08001245
robertphillips5139e502016-07-19 05:10:40 -07001246 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001247 paint);
1248
robertphillips5139e502016-07-19 05:10:40 -07001249 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001250 {
reed70ee31b2015-12-10 13:44:45 -08001251 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001252 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001253 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001254 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001255 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001256 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1257 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001258 SkErrorInternals::SetError(kInternalError_SkError,
1259 "Unable to create device for layer.");
1260 return;
reed61f501f2015-04-29 08:34:00 -07001261 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001262 }
robertphillips5139e502016-07-19 05:10:40 -07001263 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001264
robertphillips5139e502016-07-19 05:10:40 -07001265 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266
1267 layer->fNext = fMCRec->fTopLayer;
1268 fMCRec->fLayer = layer;
1269 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001270
1271 if (rec.fBackdrop) {
1272 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1273 fMCRec->fMatrix, this->getClipStack());
1274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275}
1276
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001277int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001278 if (0xFF == alpha) {
1279 return this->saveLayer(bounds, nullptr);
1280 } else {
1281 SkPaint tmpPaint;
1282 tmpPaint.setAlpha(alpha);
1283 return this->saveLayer(bounds, &tmpPaint);
1284 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001285}
1286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287void SkCanvas::internalRestore() {
1288 SkASSERT(fMCStack.count() != 0);
1289
1290 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291
reed687fa1c2015-04-07 08:00:56 -07001292 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001293
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001294 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 DeviceCM* layer = fMCRec->fLayer; // may be null
1296 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001297 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298
1299 // now do the normal restore()
1300 fMCRec->~MCRec(); // balanced in save()
1301 fMCStack.pop_back();
1302 fMCRec = (MCRec*)fMCStack.back();
1303
1304 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1305 since if we're being recorded, we don't want to record this (the
1306 recorder will have already recorded the restore).
1307 */
bsalomon49f085d2014-09-05 13:34:00 -07001308 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001310 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001311 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001312 // restore what we smashed in internalSaveLayer
1313 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001314 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001316 delete layer;
reedb679ca82015-04-07 04:40:48 -07001317 } else {
1318 // we're at the root
reeda499f902015-05-01 09:34:31 -07001319 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001320 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001321 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001323 }
msarettfbfa2582016-08-12 08:29:08 -07001324
1325 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001326 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001327 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1328 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329}
1330
reede8f30622016-03-23 18:59:25 -07001331sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001332 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001333 props = &fProps;
1334 }
1335 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001336}
1337
reede8f30622016-03-23 18:59:25 -07001338sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001339 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001340 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001341}
1342
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001343SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001344 return this->onImageInfo();
1345}
1346
1347SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001348 SkBaseDevice* dev = this->getDevice();
1349 if (dev) {
1350 return dev->imageInfo();
1351 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001352 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001353 }
1354}
1355
brianosman898235c2016-04-06 07:38:23 -07001356bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001357 return this->onGetProps(props);
1358}
1359
1360bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001361 SkBaseDevice* dev = this->getDevice();
1362 if (dev) {
1363 if (props) {
1364 *props = fProps;
1365 }
1366 return true;
1367 } else {
1368 return false;
1369 }
1370}
1371
reed6ceeebd2016-03-09 14:26:26 -08001372#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001374 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001375 if (this->peekPixels(&pmap)) {
1376 if (info) {
1377 *info = pmap.info();
1378 }
1379 if (rowBytes) {
1380 *rowBytes = pmap.rowBytes();
1381 }
1382 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001383 }
reed6ceeebd2016-03-09 14:26:26 -08001384 return nullptr;
1385}
1386#endif
1387
1388bool SkCanvas::peekPixels(SkPixmap* pmap) {
1389 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001390}
1391
reed884e97c2015-05-26 11:31:54 -07001392bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001393 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001394 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001395}
1396
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001397void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001398 SkPixmap pmap;
1399 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001400 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001401 }
1402 if (info) {
1403 *info = pmap.info();
1404 }
1405 if (rowBytes) {
1406 *rowBytes = pmap.rowBytes();
1407 }
1408 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001409 *origin = this->getTopDevice(false)->getOrigin();
1410 }
reed884e97c2015-05-26 11:31:54 -07001411 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001412}
1413
reed884e97c2015-05-26 11:31:54 -07001414bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001415 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001416 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001417}
1418
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420
reed7503d602016-07-15 14:23:29 -07001421void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001423 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 paint = &tmp;
1425 }
reed@google.com4b226022011-01-11 18:32:13 +00001426
reed@google.com8926b162012-03-23 15:36:36 +00001427 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001428
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001430 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001431 paint = &looper.paint();
1432 SkImageFilter* filter = paint->getImageFilter();
1433 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001434 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001435 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001436 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001437 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001438 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 }
reeda2217ef2016-07-20 06:04:34 -07001440
reed@google.com4e2b3d32011-04-07 14:18:59 +00001441 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442}
1443
reed32704672015-12-16 08:27:10 -08001444/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001445
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001446void SkCanvas::translate(SkScalar dx, SkScalar dy) {
mtkleincbdf0072016-08-19 09:05:27 -07001447 this->checkForDeferredSave();
1448 fDeviceCMDirty = true;
1449 fMCRec->fMatrix.preTranslate(dx,dy);
1450
1451 // Translate shouldn't affect the is-scale-translateness of the matrix.
1452 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
1453
1454 this->didTranslate(dx,dy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455}
1456
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001457void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001458 SkMatrix m;
1459 m.setScale(sx, sy);
1460 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001464 SkMatrix m;
1465 m.setRotate(degrees);
1466 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467}
1468
bungeman7438bfc2016-07-12 15:01:19 -07001469void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1470 SkMatrix m;
1471 m.setRotate(degrees, px, py);
1472 this->concat(m);
1473}
1474
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001475void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001476 SkMatrix m;
1477 m.setSkew(sx, sy);
1478 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001479}
1480
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001481void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001482 if (matrix.isIdentity()) {
1483 return;
1484 }
1485
reed2ff1fce2014-12-11 07:07:37 -08001486 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001488 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001489 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001490 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001491}
1492
reed8c30a812016-04-20 16:36:51 -07001493void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001495 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001496 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001497}
1498
1499void SkCanvas::setMatrix(const SkMatrix& matrix) {
1500 this->checkForDeferredSave();
1501 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001502 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503}
1504
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001506 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507}
1508
vjiaoblack95302da2016-07-21 10:25:54 -07001509#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001510void SkCanvas::translateZ(SkScalar z) {
1511 this->checkForDeferredSave();
1512 this->fMCRec->fCurDrawDepth += z;
1513 this->didTranslateZ(z);
1514}
1515
1516SkScalar SkCanvas::getZ() const {
1517 return this->fMCRec->fCurDrawDepth;
1518}
1519
vjiaoblack95302da2016-07-21 10:25:54 -07001520void SkCanvas::setLights(sk_sp<SkLights> lights) {
1521 this->fLights = lights;
1522}
1523
1524sk_sp<SkLights> SkCanvas::getLights() const {
1525 return this->fLights;
1526}
1527#endif
1528
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529//////////////////////////////////////////////////////////////////////////////
1530
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001531void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001532 if (!fAllowSoftClip) {
1533 doAA = false;
1534 }
1535
1536#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1537 // Check if we can quick-accept the clip call (and do nothing)
1538 //
reed74467162016-06-30 08:15:35 -07001539 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
halcanaryc5769b22016-08-10 07:13:21 -07001540 SkRect devR;
1541 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001542 // NOTE: this check is CTM specific, since we might round differently with a different
1543 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1544 // applied later (i.e. if this is going into a picture).
1545 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1546#if 0
1547 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1548 rect.left(), rect.top(), rect.right(), rect.bottom());
1549#endif
1550 return;
1551 }
1552 }
1553#endif
1554
reed2ff1fce2014-12-11 07:07:37 -08001555 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1557 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001558}
1559
1560void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001561#ifdef SK_ENABLE_CLIP_QUICKREJECT
1562 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001563 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001564 return;
reed@google.comda17f752012-08-16 18:27:05 +00001565 }
1566
reed@google.com3b3e8952012-08-16 20:53:31 +00001567 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001568 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001569 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001570
reed687fa1c2015-04-07 08:00:56 -07001571 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001572 (void)fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001573 fDeviceClipBounds.setEmpty();
reed2d1afab2016-06-29 14:33:11 -07001574 return;
reed@google.comda17f752012-08-16 18:27:05 +00001575 }
1576 }
1577#endif
1578
reed74467162016-06-30 08:15:35 -07001579 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001580 SkRect devR;
reed74467162016-06-30 08:15:35 -07001581 if (isScaleTrans) {
halcanaryc5769b22016-08-10 07:13:21 -07001582 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001583 }
bsalomonac8cabd2015-11-20 18:53:07 -08001584
reed2d1afab2016-06-29 14:33:11 -07001585#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001586 if (SkRegion::kIntersect_Op == op &&
1587 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001588 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001589 {
1590 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1591#if 0
1592 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1593 rect.left(), rect.top(), rect.right(), rect.bottom());
1594#endif
1595 return;
1596 }
1597 }
reed2d1afab2016-06-29 14:33:11 -07001598#endif
reedc64eff52015-11-21 12:39:45 -08001599
1600 AutoValidateClip avc(this);
1601
1602 fDeviceCMDirty = true;
reedc64eff52015-11-21 12:39:45 -08001603
reed74467162016-06-30 08:15:35 -07001604 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001605 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1606 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001607 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001609 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001610 // and clip against that, since it can handle any matrix. However, to
1611 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1612 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 SkPath path;
1614
1615 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 }
msarettfbfa2582016-08-12 08:29:08 -07001618
1619 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620}
1621
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001622void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001623 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001624 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001625 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1627 } else {
1628 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001629 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001630}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001631
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001632void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001633 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001634 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001635 AutoValidateClip avc(this);
1636
1637 fDeviceCMDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001638 if (!fAllowSoftClip) {
1639 edgeStyle = kHard_ClipEdgeStyle;
1640 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001641
reed687fa1c2015-04-07 08:00:56 -07001642 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001643
senorblancoafc7cce2016-02-02 18:44:15 -08001644 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001645 kSoft_ClipEdgeStyle == edgeStyle);
msarettfbfa2582016-08-12 08:29:08 -07001646 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001647 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001648 }
1649
1650 SkPath path;
1651 path.addRRect(rrect);
1652 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001654}
1655
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001656void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001657 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001658 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001659
1660 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1661 SkRect r;
1662 if (path.isRect(&r)) {
1663 this->onClipRect(r, op, edgeStyle);
1664 return;
1665 }
1666 SkRRect rrect;
1667 if (path.isOval(&r)) {
1668 rrect.setOval(r);
1669 this->onClipRRect(rrect, op, edgeStyle);
1670 return;
1671 }
1672 if (path.isRRect(&rrect)) {
1673 this->onClipRRect(rrect, op, edgeStyle);
1674 return;
1675 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001676 }
robertphillips39f05382015-11-24 09:30:12 -08001677
1678 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679}
1680
1681void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001682#ifdef SK_ENABLE_CLIP_QUICKREJECT
1683 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001684 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001685 return;
reed@google.comda17f752012-08-16 18:27:05 +00001686 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001687
reed@google.com3b3e8952012-08-16 20:53:31 +00001688 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001689 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001690 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001691
reed687fa1c2015-04-07 08:00:56 -07001692 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001693 (void)fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001694 fDeviceClipBounds.setEmpty();
reed2d1afab2016-06-29 14:33:11 -07001695 return;
reed@google.comda17f752012-08-16 18:27:05 +00001696 }
1697 }
1698#endif
1699
reed@google.com5c3d1472011-02-22 19:12:23 +00001700 AutoValidateClip avc(this);
1701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 fDeviceCMDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001703 if (!fAllowSoftClip) {
1704 edgeStyle = kHard_ClipEdgeStyle;
1705 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706
1707 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001708 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709
reed@google.comfe701122011-11-08 19:41:23 +00001710 // Check if the transfomation, or the original path itself
1711 // made us empty. Note this can also happen if we contained NaN
1712 // values. computing the bounds detects this, and will set our
1713 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1714 if (devPath.getBounds().isEmpty()) {
1715 // resetting the path will remove any NaN or other wanky values
1716 // that might upset our scan converter.
1717 devPath.reset();
1718 }
1719
reed@google.com5c3d1472011-02-22 19:12:23 +00001720 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001721 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001722
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001723 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001724 bool clipIsAA = getClipStack()->asPath(&devPath);
1725 if (clipIsAA) {
1726 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001727 }
fmalita1a481fe2015-02-04 07:39:34 -08001728
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001729 op = SkRegion::kReplace_Op;
1730 }
1731
senorblancoafc7cce2016-02-02 18:44:15 -08001732 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
msarettfbfa2582016-08-12 08:29:08 -07001733 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734}
1735
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001736void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001737 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001738 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001739}
1740
1741void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001742 AutoValidateClip avc(this);
1743
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001745
reed@google.com5c3d1472011-02-22 19:12:23 +00001746 // todo: signal fClipStack that we have a region, and therefore (I guess)
1747 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001748 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001749
reed1f836ee2014-07-07 07:49:34 -07001750 fMCRec->fRasterClip.op(rgn, op);
msarettfbfa2582016-08-12 08:29:08 -07001751 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752}
1753
reed@google.com819c9212011-02-23 18:56:55 +00001754#ifdef SK_DEBUG
1755void SkCanvas::validateClip() const {
1756 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001757 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001758 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001759 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001760 return;
1761 }
1762
reed@google.com819c9212011-02-23 18:56:55 +00001763 SkIRect ir;
1764 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001765 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001766
reed687fa1c2015-04-07 08:00:56 -07001767 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001768 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001769 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001770 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001771 case SkClipStack::Element::kRect_Type:
1772 element->getRect().round(&ir);
1773 tmpClip.op(ir, element->getOp());
1774 break;
1775 case SkClipStack::Element::kEmpty_Type:
1776 tmpClip.setEmpty();
1777 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001778 default: {
1779 SkPath path;
1780 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001781 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001782 break;
1783 }
reed@google.com819c9212011-02-23 18:56:55 +00001784 }
1785 }
reed@google.com819c9212011-02-23 18:56:55 +00001786}
1787#endif
1788
reed@google.com90c07ea2012-04-13 13:50:27 +00001789void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001790 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001791 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001792
halcanary96fcdcc2015-08-27 07:41:13 -07001793 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001794 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001795 }
1796}
1797
reed@google.com5c3d1472011-02-22 19:12:23 +00001798///////////////////////////////////////////////////////////////////////////////
1799
reed@google.com754de5f2014-02-24 19:38:20 +00001800bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001801 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001802}
1803
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001804bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001805 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001806}
1807
msarettfbfa2582016-08-12 08:29:08 -07001808static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1809#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1810 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1811 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1812 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1813 return 0xF != _mm_movemask_ps(mask);
1814#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1815 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1816 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1817 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1818 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1819#else
1820 SkRect devRectAsRect;
1821 SkRect devClipAsRect;
1822 devRect.store(&devRectAsRect.fLeft);
1823 devClip.store(&devClipAsRect.fLeft);
1824 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1825#endif
1826}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001827
msarettfbfa2582016-08-12 08:29:08 -07001828// It's important for this function to not be inlined. Otherwise the compiler will share code
1829// between the fast path and the slow path, resulting in two slow paths.
1830static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1831 const SkMatrix& matrix) {
1832 SkRect deviceRect;
1833 matrix.mapRect(&deviceRect, src);
1834 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1835}
1836
1837bool SkCanvas::quickReject(const SkRect& src) const {
1838#ifdef SK_DEBUG
1839 // Verify that fDeviceClipBounds are set properly.
1840 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001841 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001842 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001843 } else {
msarettfbfa2582016-08-12 08:29:08 -07001844 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 }
msarettfbfa2582016-08-12 08:29:08 -07001846
msarett9637ea92016-08-18 14:03:30 -07001847 // Verify that fIsScaleTranslate is set properly.
1848 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001849#endif
1850
msarett9637ea92016-08-18 14:03:30 -07001851 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001852 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1853 }
1854
1855 // We inline the implementation of mapScaleTranslate() for the fast path.
1856 float sx = fMCRec->fMatrix.getScaleX();
1857 float sy = fMCRec->fMatrix.getScaleY();
1858 float tx = fMCRec->fMatrix.getTranslateX();
1859 float ty = fMCRec->fMatrix.getTranslateY();
1860 Sk4f scale(sx, sy, sx, sy);
1861 Sk4f trans(tx, ty, tx, ty);
1862
1863 // Apply matrix.
1864 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1865
1866 // Make sure left < right, top < bottom.
1867 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1868 Sk4f min = Sk4f::Min(ltrb, rblt);
1869 Sk4f max = Sk4f::Max(ltrb, rblt);
1870 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1871 // ARM this sequence generates the fastest (a single instruction).
1872 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1873
1874 // Check if the device rect is NaN or outside the clip.
1875 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001876}
1877
reed@google.com3b3e8952012-08-16 20:53:31 +00001878bool SkCanvas::quickReject(const SkPath& path) const {
1879 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880}
1881
reed@google.com3b3e8952012-08-16 20:53:31 +00001882bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001883 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001884 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885 return false;
1886 }
1887
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001888 SkMatrix inverse;
1889 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001890 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001891 if (bounds) {
1892 bounds->setEmpty();
1893 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001894 return false;
1895 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896
bsalomon49f085d2014-09-05 13:34:00 -07001897 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001898 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001899 // adjust it outwards in case we are antialiasing
1900 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001901
reed@google.com8f4d2302013-12-17 16:44:46 +00001902 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1903 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904 inverse.mapRect(bounds, r);
1905 }
1906 return true;
1907}
1908
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001909bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001910 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001911 if (clip.isEmpty()) {
1912 if (bounds) {
1913 bounds->setEmpty();
1914 }
1915 return false;
1916 }
1917
bsalomon49f085d2014-09-05 13:34:00 -07001918 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001919 *bounds = clip.getBounds();
1920 }
1921 return true;
1922}
1923
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001925 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926}
1927
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001928const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001929 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001930}
1931
robertphillips175dd9b2016-04-28 14:32:04 -07001932GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001933 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001934 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001935}
1936
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001937GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001938 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001939 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001940}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001941
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001942void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1943 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001944 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001945 if (outer.isEmpty()) {
1946 return;
1947 }
1948 if (inner.isEmpty()) {
1949 this->drawRRect(outer, paint);
1950 return;
1951 }
1952
1953 // We don't have this method (yet), but technically this is what we should
1954 // be able to assert...
1955 // SkASSERT(outer.contains(inner));
1956 //
1957 // For now at least check for containment of bounds
1958 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1959
1960 this->onDrawDRRect(outer, inner, paint);
1961}
1962
reed41af9662015-01-05 07:49:08 -08001963// These need to stop being virtual -- clients need to override the onDraw... versions
1964
1965void SkCanvas::drawPaint(const SkPaint& paint) {
1966 this->onDrawPaint(paint);
1967}
1968
1969void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1970 this->onDrawRect(r, paint);
1971}
1972
1973void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1974 this->onDrawOval(r, paint);
1975}
1976
1977void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1978 this->onDrawRRect(rrect, paint);
1979}
1980
1981void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1982 this->onDrawPoints(mode, count, pts, paint);
1983}
1984
1985void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1986 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1987 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1988 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1989 indices, indexCount, paint);
1990}
1991
1992void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1993 this->onDrawPath(path, paint);
1994}
1995
reeda85d4d02015-05-06 12:56:48 -07001996void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001997 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001998 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001999}
2000
reede47829b2015-08-06 10:02:53 -07002001void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2002 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002003 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002004 if (dst.isEmpty() || src.isEmpty()) {
2005 return;
2006 }
2007 this->onDrawImageRect(image, &src, dst, paint, constraint);
2008}
reed41af9662015-01-05 07:49:08 -08002009
reed84984ef2015-07-17 07:09:43 -07002010void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2011 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002012 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002013 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002014}
2015
reede47829b2015-08-06 10:02:53 -07002016void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
2017 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002018 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002019 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
2020 constraint);
2021}
reede47829b2015-08-06 10:02:53 -07002022
reed4c21dc52015-06-25 12:32:03 -07002023void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2024 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002025 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002026 if (dst.isEmpty()) {
2027 return;
2028 }
msarett552bca92016-08-03 06:53:26 -07002029 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
2030 this->onDrawImageNine(image, center, dst, paint);
2031 } else {
reede47829b2015-08-06 10:02:53 -07002032 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002033 }
reed4c21dc52015-06-25 12:32:03 -07002034}
2035
msarett16882062016-08-16 09:31:08 -07002036void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2037 const SkPaint* paint) {
2038 RETURN_ON_NULL(image);
2039 if (dst.isEmpty()) {
2040 return;
2041 }
2042 if (SkLatticeIter::Valid(image->width(), image->height(), lattice)) {
2043 this->onDrawImageLattice(image, lattice, dst, paint);
2044 } else {
2045 this->drawImageRect(image, dst, paint);
2046 }
2047}
2048
reed41af9662015-01-05 07:49:08 -08002049void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002050 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002051 return;
2052 }
reed41af9662015-01-05 07:49:08 -08002053 this->onDrawBitmap(bitmap, dx, dy, paint);
2054}
2055
reede47829b2015-08-06 10:02:53 -07002056void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002057 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002058 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002059 return;
2060 }
reede47829b2015-08-06 10:02:53 -07002061 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002062}
2063
reed84984ef2015-07-17 07:09:43 -07002064void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2065 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002066 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002067}
2068
reede47829b2015-08-06 10:02:53 -07002069void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2070 SrcRectConstraint constraint) {
2071 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2072 constraint);
2073}
reede47829b2015-08-06 10:02:53 -07002074
reed41af9662015-01-05 07:49:08 -08002075void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2076 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002077 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002078 return;
2079 }
msarett552bca92016-08-03 06:53:26 -07002080 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
2081 this->onDrawBitmapNine(bitmap, center, dst, paint);
2082 } else {
reeda5517e22015-07-14 10:54:12 -07002083 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002084 }
reed41af9662015-01-05 07:49:08 -08002085}
2086
msarettc573a402016-08-02 08:05:56 -07002087void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2088 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07002089 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002090 return;
2091 }
msarett16882062016-08-16 09:31:08 -07002092 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) {
2093 this->onDrawBitmapLattice(bitmap, lattice, dst, paint);
msarett552bca92016-08-03 06:53:26 -07002094 } else {
msarett16882062016-08-16 09:31:08 -07002095 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002096 }
msarettc573a402016-08-02 08:05:56 -07002097}
2098
reed71c3c762015-06-24 10:29:17 -07002099void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2100 const SkColor colors[], int count, SkXfermode::Mode mode,
2101 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002102 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002103 if (count <= 0) {
2104 return;
2105 }
2106 SkASSERT(atlas);
2107 SkASSERT(xform);
2108 SkASSERT(tex);
2109 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2110}
2111
reedf70b5312016-03-04 16:36:20 -08002112void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2113 if (key) {
2114 this->onDrawAnnotation(rect, key, value);
2115 }
2116}
2117
reede47829b2015-08-06 10:02:53 -07002118void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2119 const SkPaint* paint, SrcRectConstraint constraint) {
2120 if (src) {
2121 this->drawImageRect(image, *src, dst, paint, constraint);
2122 } else {
2123 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2124 dst, paint, constraint);
2125 }
2126}
2127void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2128 const SkPaint* paint, SrcRectConstraint constraint) {
2129 if (src) {
2130 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2131 } else {
2132 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2133 dst, paint, constraint);
2134 }
2135}
2136
tomhudsoncb3bd182016-05-18 07:24:16 -07002137void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2138 SkIRect layer_bounds = this->getTopLayerBounds();
2139 if (matrix) {
2140 *matrix = this->getTotalMatrix();
2141 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2142 }
2143 if (clip_bounds) {
2144 this->getClipDeviceBounds(clip_bounds);
2145 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2146 }
2147}
2148
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149//////////////////////////////////////////////////////////////////////////////
2150// These are the virtual drawing methods
2151//////////////////////////////////////////////////////////////////////////////
2152
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002153void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002154 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002155 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2156 }
2157}
2158
reed41af9662015-01-05 07:49:08 -08002159void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002160 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002161 this->internalDrawPaint(paint);
2162}
2163
2164void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002165 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166
2167 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002168 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169 }
2170
reed@google.com4e2b3d32011-04-07 14:18:59 +00002171 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172}
2173
reed41af9662015-01-05 07:49:08 -08002174void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2175 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002176 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177 if ((long)count <= 0) {
2178 return;
2179 }
2180
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002181 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002182 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002183 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002184 // special-case 2 points (common for drawing a single line)
2185 if (2 == count) {
2186 r.set(pts[0], pts[1]);
2187 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002188 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002189 }
senorblanco87e066e2015-10-28 11:23:36 -07002190 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2191 return;
2192 }
2193 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002194 }
reed@google.coma584aed2012-05-16 14:06:02 +00002195
halcanary96fcdcc2015-08-27 07:41:13 -07002196 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002198 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002199
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002201 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202 }
reed@google.com4b226022011-01-11 18:32:13 +00002203
reed@google.com4e2b3d32011-04-07 14:18:59 +00002204 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205}
2206
reed4a167172016-08-18 17:15:25 -07002207static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2208 return ((intptr_t)paint.getImageFilter() |
2209#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2210 (intptr_t)canvas->getDrawFilter() |
2211#endif
2212 (intptr_t)paint.getLooper() ) != 0;
2213}
2214
reed41af9662015-01-05 07:49:08 -08002215void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002216 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002217 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002218 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002220 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2221 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2222 SkRect tmp(r);
2223 tmp.sort();
2224
senorblanco87e066e2015-10-28 11:23:36 -07002225 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2226 return;
2227 }
2228 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002229 }
reed@google.com4b226022011-01-11 18:32:13 +00002230
reed4a167172016-08-18 17:15:25 -07002231 if (needs_autodrawlooper(this, paint)) {
2232 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233
reed4a167172016-08-18 17:15:25 -07002234 while (iter.next()) {
2235 iter.fDevice->drawRect(iter, r, looper.paint());
2236 }
2237
2238 LOOPER_END
2239 } else {
2240 this->predrawNotify(bounds, &paint, false);
2241 SkDrawIter iter(this);
2242 while (iter.next()) {
2243 iter.fDevice->drawRect(iter, r, paint);
2244 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246}
2247
reed41af9662015-01-05 07:49:08 -08002248void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002249 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002250 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002251 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002252 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002253 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2254 return;
2255 }
2256 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002257 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002258
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002259 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002260
2261 while (iter.next()) {
2262 iter.fDevice->drawOval(iter, oval, looper.paint());
2263 }
2264
2265 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002266}
2267
reed41af9662015-01-05 07:49:08 -08002268void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002269 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002270 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002271 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002272 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002273 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2274 return;
2275 }
2276 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002277 }
2278
2279 if (rrect.isRect()) {
2280 // call the non-virtual version
2281 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002282 return;
2283 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002284 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002285 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2286 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002287 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002288
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002289 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002290
2291 while (iter.next()) {
2292 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2293 }
2294
2295 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002296}
2297
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002298void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2299 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002300 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002301 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002302 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002303 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2304 return;
2305 }
2306 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002307 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002308
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002309 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002310
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002311 while (iter.next()) {
2312 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2313 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002314
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002315 LOOPER_END
2316}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002317
reed41af9662015-01-05 07:49:08 -08002318void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002319 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002320 if (!path.isFinite()) {
2321 return;
2322 }
2323
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002324 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002325 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002326 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002327 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002328 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2329 return;
2330 }
2331 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002332 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002333
2334 const SkRect& r = path.getBounds();
2335 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002336 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002337 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002338 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002339 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002340 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002341
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002342 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343
2344 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002345 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002346 }
2347
reed@google.com4e2b3d32011-04-07 14:18:59 +00002348 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002349}
2350
reed262a71b2015-12-05 13:07:27 -08002351bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002352 if (!paint.getImageFilter()) {
2353 return false;
2354 }
2355
2356 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002357 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002358 return false;
2359 }
2360
2361 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2362 // Once we can filter and the filter will return a result larger than itself, we should be
2363 // able to remove this constraint.
2364 // skbug.com/4526
2365 //
2366 SkPoint pt;
2367 ctm.mapXY(x, y, &pt);
2368 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2369 return ir.contains(fMCRec->fRasterClip.getBounds());
2370}
2371
reeda85d4d02015-05-06 12:56:48 -07002372void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002373 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002374 SkRect bounds = SkRect::MakeXYWH(x, y,
2375 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002376 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002377 SkRect tmp = bounds;
2378 if (paint) {
2379 paint->computeFastBounds(tmp, &tmp);
2380 }
2381 if (this->quickReject(tmp)) {
2382 return;
2383 }
reeda85d4d02015-05-06 12:56:48 -07002384 }
halcanary9d524f22016-03-29 09:03:52 -07002385
reeda85d4d02015-05-06 12:56:48 -07002386 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002387 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002388 paint = lazy.init();
2389 }
reed262a71b2015-12-05 13:07:27 -08002390
reeda2217ef2016-07-20 06:04:34 -07002391 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002392 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2393 *paint);
2394 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002395 special = this->getDevice()->makeSpecial(image);
2396 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002397 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002398 }
2399 }
2400
reed262a71b2015-12-05 13:07:27 -08002401 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2402
reeda85d4d02015-05-06 12:56:48 -07002403 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002404 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002405 if (special) {
2406 SkPoint pt;
2407 iter.fMatrix->mapXY(x, y, &pt);
2408 iter.fDevice->drawSpecial(iter, special.get(),
2409 SkScalarRoundToInt(pt.fX),
2410 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002411 } else {
2412 iter.fDevice->drawImage(iter, image, x, y, pnt);
2413 }
reeda85d4d02015-05-06 12:56:48 -07002414 }
halcanary9d524f22016-03-29 09:03:52 -07002415
reeda85d4d02015-05-06 12:56:48 -07002416 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002417}
2418
reed41af9662015-01-05 07:49:08 -08002419void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002420 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002421 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002422 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002423 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002424 if (paint) {
2425 paint->computeFastBounds(dst, &storage);
2426 }
2427 if (this->quickReject(storage)) {
2428 return;
2429 }
reeda85d4d02015-05-06 12:56:48 -07002430 }
2431 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002432 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002433 paint = lazy.init();
2434 }
halcanary9d524f22016-03-29 09:03:52 -07002435
senorblancoc41e7e12015-12-07 12:51:30 -08002436 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002437 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002438
reeda85d4d02015-05-06 12:56:48 -07002439 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002440 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002441 }
halcanary9d524f22016-03-29 09:03:52 -07002442
reeda85d4d02015-05-06 12:56:48 -07002443 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002444}
2445
reed41af9662015-01-05 07:49:08 -08002446void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002447 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448 SkDEBUGCODE(bitmap.validate();)
2449
reed33366972015-10-08 09:22:02 -07002450 if (bitmap.drawsNothing()) {
2451 return;
2452 }
2453
2454 SkLazyPaint lazy;
2455 if (nullptr == paint) {
2456 paint = lazy.init();
2457 }
2458
2459 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2460
2461 SkRect storage;
2462 const SkRect* bounds = nullptr;
2463 if (paint->canComputeFastBounds()) {
2464 bitmap.getBounds(&storage);
2465 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002466 SkRect tmp = storage;
2467 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2468 return;
2469 }
2470 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471 }
reed@google.com4b226022011-01-11 18:32:13 +00002472
reeda2217ef2016-07-20 06:04:34 -07002473 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002474 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2475 *paint);
2476 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002477 special = this->getDevice()->makeSpecial(bitmap);
2478 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002479 drawAsSprite = false;
2480 }
2481 }
2482
reed262a71b2015-12-05 13:07:27 -08002483 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002484
2485 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002486 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002487 if (special) {
reed262a71b2015-12-05 13:07:27 -08002488 SkPoint pt;
2489 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002490 iter.fDevice->drawSpecial(iter, special.get(),
2491 SkScalarRoundToInt(pt.fX),
2492 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002493 } else {
2494 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2495 }
reed33366972015-10-08 09:22:02 -07002496 }
msarettfbfa2582016-08-12 08:29:08 -07002497
reed33366972015-10-08 09:22:02 -07002498 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002499}
2500
reed@google.com9987ec32011-09-07 11:57:52 +00002501// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002502void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002503 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002504 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002505 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002506 return;
2507 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002508
halcanary96fcdcc2015-08-27 07:41:13 -07002509 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002510 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002511 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2512 return;
2513 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002514 }
reed@google.com3d608122011-11-21 15:16:16 +00002515
reed@google.com33535f32012-09-25 15:37:50 +00002516 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002517 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002518 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002519 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002520
senorblancoc41e7e12015-12-07 12:51:30 -08002521 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002522 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002523
reed@google.com33535f32012-09-25 15:37:50 +00002524 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002525 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002526 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002527
reed@google.com33535f32012-09-25 15:37:50 +00002528 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002529}
2530
reed41af9662015-01-05 07:49:08 -08002531void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002532 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002533 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002534 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002535 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002536}
2537
reed4c21dc52015-06-25 12:32:03 -07002538void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2539 const SkPaint* paint) {
2540 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002541
halcanary96fcdcc2015-08-27 07:41:13 -07002542 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002543 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002544 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2545 return;
2546 }
reed@google.com3d608122011-11-21 15:16:16 +00002547 }
halcanary9d524f22016-03-29 09:03:52 -07002548
reed4c21dc52015-06-25 12:32:03 -07002549 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002550 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002551 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002552 }
halcanary9d524f22016-03-29 09:03:52 -07002553
senorblancoc41e7e12015-12-07 12:51:30 -08002554 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002555
reed4c21dc52015-06-25 12:32:03 -07002556 while (iter.next()) {
2557 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002558 }
halcanary9d524f22016-03-29 09:03:52 -07002559
reed4c21dc52015-06-25 12:32:03 -07002560 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002561}
2562
reed41af9662015-01-05 07:49:08 -08002563void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2564 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002565 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002566 SkDEBUGCODE(bitmap.validate();)
2567
halcanary96fcdcc2015-08-27 07:41:13 -07002568 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002569 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002570 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2571 return;
2572 }
reed4c21dc52015-06-25 12:32:03 -07002573 }
halcanary9d524f22016-03-29 09:03:52 -07002574
reed4c21dc52015-06-25 12:32:03 -07002575 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002576 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002577 paint = lazy.init();
2578 }
halcanary9d524f22016-03-29 09:03:52 -07002579
senorblancoc41e7e12015-12-07 12:51:30 -08002580 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002581
reed4c21dc52015-06-25 12:32:03 -07002582 while (iter.next()) {
2583 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2584 }
halcanary9d524f22016-03-29 09:03:52 -07002585
reed4c21dc52015-06-25 12:32:03 -07002586 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002587}
2588
msarett16882062016-08-16 09:31:08 -07002589void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2590 const SkPaint* paint) {
2591 if (nullptr == paint || paint->canComputeFastBounds()) {
2592 SkRect storage;
2593 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2594 return;
2595 }
2596 }
2597
2598 SkLazyPaint lazy;
2599 if (nullptr == paint) {
2600 paint = lazy.init();
2601 }
2602
2603 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2604
2605 while (iter.next()) {
2606 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2607 }
2608
2609 LOOPER_END
2610}
2611
2612void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2613 const SkRect& dst, const SkPaint* paint) {
2614 if (nullptr == paint || paint->canComputeFastBounds()) {
2615 SkRect storage;
2616 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2617 return;
2618 }
2619 }
2620
2621 SkLazyPaint lazy;
2622 if (nullptr == paint) {
2623 paint = lazy.init();
2624 }
2625
2626 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2627
2628 while (iter.next()) {
2629 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2630 }
2631
2632 LOOPER_END
2633}
2634
reed@google.comf67e4cf2011-03-15 20:56:58 +00002635class SkDeviceFilteredPaint {
2636public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002637 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002638 uint32_t filteredFlags = device->filterTextFlags(paint);
2639 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002640 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002641 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002642 fPaint = newPaint;
2643 } else {
2644 fPaint = &paint;
2645 }
2646 }
2647
reed@google.comf67e4cf2011-03-15 20:56:58 +00002648 const SkPaint& paint() const { return *fPaint; }
2649
2650private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002651 const SkPaint* fPaint;
2652 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002653};
2654
bungeman@google.com52c748b2011-08-22 21:30:43 +00002655void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2656 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002657 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002658 draw.fDevice->drawRect(draw, r, paint);
2659 } else {
2660 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002661 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002662 draw.fDevice->drawRect(draw, r, p);
2663 }
2664}
2665
2666void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2667 const char text[], size_t byteLength,
2668 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002669 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002670
2671 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002672 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002673 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002674 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002675 return;
2676 }
2677
2678 SkScalar width = 0;
2679 SkPoint start;
2680
2681 start.set(0, 0); // to avoid warning
2682 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2683 SkPaint::kStrikeThruText_Flag)) {
2684 width = paint.measureText(text, byteLength);
2685
2686 SkScalar offsetX = 0;
2687 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2688 offsetX = SkScalarHalf(width);
2689 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2690 offsetX = width;
2691 }
2692 start.set(x - offsetX, y);
2693 }
2694
2695 if (0 == width) {
2696 return;
2697 }
2698
2699 uint32_t flags = paint.getFlags();
2700
2701 if (flags & (SkPaint::kUnderlineText_Flag |
2702 SkPaint::kStrikeThruText_Flag)) {
2703 SkScalar textSize = paint.getTextSize();
2704 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2705 SkRect r;
2706
2707 r.fLeft = start.fX;
2708 r.fRight = start.fX + width;
2709
2710 if (flags & SkPaint::kUnderlineText_Flag) {
2711 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2712 start.fY);
2713 r.fTop = offset;
2714 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002715 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002716 }
2717 if (flags & SkPaint::kStrikeThruText_Flag) {
2718 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2719 start.fY);
2720 r.fTop = offset;
2721 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002722 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002723 }
2724 }
2725}
2726
reed@google.come0d9ce82014-04-23 04:00:17 +00002727void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2728 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002729 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002730
2731 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002732 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002733 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002734 DrawTextDecorations(iter, dfp.paint(),
2735 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002736 }
2737
reed@google.com4e2b3d32011-04-07 14:18:59 +00002738 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002739}
2740
reed@google.come0d9ce82014-04-23 04:00:17 +00002741void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2742 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002743 SkPoint textOffset = SkPoint::Make(0, 0);
2744
halcanary96fcdcc2015-08-27 07:41:13 -07002745 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002746
reed@android.com8a1c16f2008-12-17 15:59:43 +00002747 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002748 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002749 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002750 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002751 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002752
reed@google.com4e2b3d32011-04-07 14:18:59 +00002753 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002754}
2755
reed@google.come0d9ce82014-04-23 04:00:17 +00002756void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2757 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002758
2759 SkPoint textOffset = SkPoint::Make(0, constY);
2760
halcanary96fcdcc2015-08-27 07:41:13 -07002761 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002762
reed@android.com8a1c16f2008-12-17 15:59:43 +00002763 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002764 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002765 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002766 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002767 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002768
reed@google.com4e2b3d32011-04-07 14:18:59 +00002769 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002770}
2771
reed@google.come0d9ce82014-04-23 04:00:17 +00002772void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2773 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002774 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002775
reed@android.com8a1c16f2008-12-17 15:59:43 +00002776 while (iter.next()) {
2777 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002778 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002779 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002780
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002781 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002782}
2783
reed45561a02016-07-07 12:47:17 -07002784void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2785 const SkRect* cullRect, const SkPaint& paint) {
2786 if (cullRect && this->quickReject(*cullRect)) {
2787 return;
2788 }
2789
2790 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2791
2792 while (iter.next()) {
2793 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2794 }
2795
2796 LOOPER_END
2797}
2798
fmalita00d5c2c2014-08-21 08:53:26 -07002799void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2800 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002801
fmalita85d5eb92015-03-04 11:20:12 -08002802 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002803 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002804 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002805 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002806 SkRect tmp;
2807 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2808 return;
2809 }
2810 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002811 }
2812
fmalita024f9962015-03-03 19:08:17 -08002813 // We cannot filter in the looper as we normally do, because the paint is
2814 // incomplete at this point (text-related attributes are embedded within blob run paints).
2815 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002816 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002817
fmalita85d5eb92015-03-04 11:20:12 -08002818 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002819
fmalitaaa1b9122014-08-28 14:32:24 -07002820 while (iter.next()) {
2821 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002822 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002823 }
2824
fmalitaaa1b9122014-08-28 14:32:24 -07002825 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002826
2827 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002828}
2829
reed@google.come0d9ce82014-04-23 04:00:17 +00002830// These will become non-virtual, so they always call the (virtual) onDraw... method
2831void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2832 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002833 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002834 if (byteLength) {
2835 this->onDrawText(text, byteLength, x, y, paint);
2836 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002837}
2838void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2839 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002840 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002841 if (byteLength) {
2842 this->onDrawPosText(text, byteLength, pos, paint);
2843 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002844}
2845void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2846 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002847 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002848 if (byteLength) {
2849 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2850 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002851}
2852void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2853 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002854 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002855 if (byteLength) {
2856 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2857 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002858}
reed45561a02016-07-07 12:47:17 -07002859void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2860 const SkRect* cullRect, const SkPaint& paint) {
2861 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2862 if (byteLength) {
2863 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2864 }
2865}
fmalita00d5c2c2014-08-21 08:53:26 -07002866void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2867 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002868 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002869 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002870 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002871}
reed@google.come0d9ce82014-04-23 04:00:17 +00002872
reed41af9662015-01-05 07:49:08 -08002873void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2874 const SkPoint verts[], const SkPoint texs[],
2875 const SkColor colors[], SkXfermode* xmode,
2876 const uint16_t indices[], int indexCount,
2877 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002878 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002879 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002880
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881 while (iter.next()) {
2882 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002883 colors, xmode, indices, indexCount,
2884 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002885 }
reed@google.com4b226022011-01-11 18:32:13 +00002886
reed@google.com4e2b3d32011-04-07 14:18:59 +00002887 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888}
2889
dandovb3c9d1c2014-08-12 08:34:29 -07002890void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2891 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002892 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002893 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002894 return;
2895 }
mtklein6cfa73a2014-08-13 13:33:49 -07002896
dandovecfff212014-08-04 10:02:00 -07002897 // Since a patch is always within the convex hull of the control points, we discard it when its
2898 // bounding rectangle is completely outside the current clip.
2899 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002900 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002901 if (this->quickReject(bounds)) {
2902 return;
2903 }
mtklein6cfa73a2014-08-13 13:33:49 -07002904
dandovb3c9d1c2014-08-12 08:34:29 -07002905 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2906}
2907
2908void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2909 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2910
halcanary96fcdcc2015-08-27 07:41:13 -07002911 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002912
dandovecfff212014-08-04 10:02:00 -07002913 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002914 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002915 }
mtklein6cfa73a2014-08-13 13:33:49 -07002916
dandovecfff212014-08-04 10:02:00 -07002917 LOOPER_END
2918}
2919
reeda8db7282015-07-07 10:22:31 -07002920void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002921 RETURN_ON_NULL(dr);
2922 if (x || y) {
2923 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2924 this->onDrawDrawable(dr, &matrix);
2925 } else {
2926 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002927 }
2928}
2929
reeda8db7282015-07-07 10:22:31 -07002930void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002931 RETURN_ON_NULL(dr);
2932 if (matrix && matrix->isIdentity()) {
2933 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002934 }
reede3b38ce2016-01-08 09:18:44 -08002935 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002936}
2937
2938void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2939 SkRect bounds = dr->getBounds();
2940 if (matrix) {
2941 matrix->mapRect(&bounds);
2942 }
2943 if (this->quickReject(bounds)) {
2944 return;
2945 }
2946 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002947}
2948
reed71c3c762015-06-24 10:29:17 -07002949void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2950 const SkColor colors[], int count, SkXfermode::Mode mode,
2951 const SkRect* cull, const SkPaint* paint) {
2952 if (cull && this->quickReject(*cull)) {
2953 return;
2954 }
2955
2956 SkPaint pnt;
2957 if (paint) {
2958 pnt = *paint;
2959 }
halcanary9d524f22016-03-29 09:03:52 -07002960
halcanary96fcdcc2015-08-27 07:41:13 -07002961 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002962 while (iter.next()) {
2963 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2964 }
2965 LOOPER_END
2966}
2967
reedf70b5312016-03-04 16:36:20 -08002968void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2969 SkASSERT(key);
2970
2971 SkPaint paint;
2972 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2973 while (iter.next()) {
2974 iter.fDevice->drawAnnotation(iter, rect, key, value);
2975 }
2976 LOOPER_END
2977}
2978
reed@android.com8a1c16f2008-12-17 15:59:43 +00002979//////////////////////////////////////////////////////////////////////////////
2980// These methods are NOT virtual, and therefore must call back into virtual
2981// methods, rather than actually drawing themselves.
2982//////////////////////////////////////////////////////////////////////////////
2983
2984void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002985 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002986 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002987 SkPaint paint;
2988
2989 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002990 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002991 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002992 }
2993 this->drawPaint(paint);
2994}
2995
reed@android.com845fdac2009-06-23 03:01:32 +00002996void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002997 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002998 SkPaint paint;
2999
3000 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00003001 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00003002 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003003 }
3004 this->drawPaint(paint);
3005}
3006
3007void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003008 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00003010
reed@android.com8a1c16f2008-12-17 15:59:43 +00003011 pt.set(x, y);
3012 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3013}
3014
3015void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08003016 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017 SkPoint pt;
3018 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00003019
reed@android.com8a1c16f2008-12-17 15:59:43 +00003020 pt.set(x, y);
3021 paint.setColor(color);
3022 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3023}
3024
3025void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
3026 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003027 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003028 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00003029
reed@android.com8a1c16f2008-12-17 15:59:43 +00003030 pts[0].set(x0, y0);
3031 pts[1].set(x1, y1);
3032 this->drawPoints(kLines_PointMode, 2, pts, paint);
3033}
3034
3035void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
3036 SkScalar right, SkScalar bottom,
3037 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003038 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003039 SkRect r;
3040
3041 r.set(left, top, right, bottom);
3042 this->drawRect(r, paint);
3043}
3044
3045void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
3046 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003047 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003048 if (radius < 0) {
3049 radius = 0;
3050 }
3051
3052 SkRect r;
3053 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003054 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003055}
3056
3057void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3058 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003059 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003060 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003061 SkRRect rrect;
3062 rrect.setRectXY(r, rx, ry);
3063 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003064 } else {
3065 this->drawRect(r, paint);
3066 }
3067}
3068
reed@android.com8a1c16f2008-12-17 15:59:43 +00003069void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3070 SkScalar sweepAngle, bool useCenter,
3071 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003072 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003073 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
3074 this->drawOval(oval, paint);
3075 } else {
3076 SkPath path;
3077 if (useCenter) {
3078 path.moveTo(oval.centerX(), oval.centerY());
3079 }
3080 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
3081 if (useCenter) {
3082 path.close();
3083 }
3084 this->drawPath(path, paint);
3085 }
3086}
3087
3088void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3089 const SkPath& path, SkScalar hOffset,
3090 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003091 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003092 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003093
reed@android.com8a1c16f2008-12-17 15:59:43 +00003094 matrix.setTranslate(hOffset, vOffset);
3095 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3096}
3097
reed@android.comf76bacf2009-05-13 14:00:33 +00003098///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003099
3100/**
3101 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3102 * against the playback cost of recursing into the subpicture to get at its actual ops.
3103 *
3104 * For now we pick a conservatively small value, though measurement (and other heuristics like
3105 * the type of ops contained) may justify changing this value.
3106 */
3107#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003108
reedd5fa1a42014-08-09 11:08:05 -07003109void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003110 RETURN_ON_NULL(picture);
3111
reed1c2c4412015-04-30 13:09:24 -07003112 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003113 if (matrix && matrix->isIdentity()) {
3114 matrix = nullptr;
3115 }
3116 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3117 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3118 picture->playback(this);
3119 } else {
3120 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003121 }
3122}
robertphillips9b14f262014-06-04 05:40:44 -07003123
reedd5fa1a42014-08-09 11:08:05 -07003124void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3125 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003126 if (!paint || paint->canComputeFastBounds()) {
3127 SkRect bounds = picture->cullRect();
3128 if (paint) {
3129 paint->computeFastBounds(bounds, &bounds);
3130 }
3131 if (matrix) {
3132 matrix->mapRect(&bounds);
3133 }
3134 if (this->quickReject(bounds)) {
3135 return;
3136 }
3137 }
3138
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003139 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003140 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003141}
3142
vjiaoblack95302da2016-07-21 10:25:54 -07003143#ifdef SK_EXPERIMENTAL_SHADOWING
3144void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3145 const SkMatrix* matrix,
3146 const SkPaint* paint) {
3147 RETURN_ON_NULL(picture);
3148
3149 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3150
3151 this->onDrawShadowedPicture(picture, matrix, paint);
3152}
3153
3154void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3155 const SkMatrix* matrix,
3156 const SkPaint* paint) {
vjiaoblack904527d2016-08-09 09:32:09 -07003157 if (!paint || paint->canComputeFastBounds()) {
3158 SkRect bounds = picture->cullRect();
3159 if (paint) {
3160 paint->computeFastBounds(bounds, &bounds);
3161 }
3162 if (matrix) {
3163 matrix->mapRect(&bounds);
3164 }
3165 if (this->quickReject(bounds)) {
3166 return;
3167 }
3168 }
3169
3170 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3171
3172 for (int i = 0; i < fLights->numLights(); ++i) {
3173 // skip over ambient lights; they don't cast shadows
3174 // lights that have shadow maps do not need updating (because lights are immutable)
3175
3176 if (SkLights::Light::kAmbient_LightType == fLights->light(i).type() ||
3177 fLights->light(i).getShadowMap() != nullptr) {
3178 continue;
3179 }
3180
3181 // TODO: compute the correct size of the depth map from the light properties
3182 // TODO: maybe add a kDepth_8_SkColorType
3183 // TODO: find actual max depth of picture
3184 SkISize shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3185 fLights->light(i), 255,
3186 picture->cullRect().width(),
3187 picture->cullRect().height());
3188
3189 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3190 kBGRA_8888_SkColorType,
3191 kOpaque_SkAlphaType);
3192
3193 // Create a new surface (that matches the backend of canvas)
3194 // for each shadow map
3195 sk_sp<SkSurface> surf(this->makeSurface(info));
3196
3197 // Wrap another SPFCanvas around the surface
3198 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3199 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3200
3201 // set the depth map canvas to have the light we're drawing.
3202 SkLights::Builder builder;
3203 builder.add(fLights->light(i));
3204 sk_sp<SkLights> curLight = builder.finish();
3205
3206 depthMapCanvas->setLights(std::move(curLight));
3207 depthMapCanvas->drawPicture(picture);
3208
3209 fLights->light(i).setShadowMap(surf->makeImageSnapshot());
3210 }
3211
3212 sk_sp<SkImage> povDepthMap;
3213 sk_sp<SkImage> diffuseMap;
3214
3215 // TODO: pass the depth to the shader in vertices, or uniforms
3216 // so we don't have to render depth and color separately
3217
3218 // povDepthMap
3219 {
3220 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003221 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3222 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003223 sk_sp<SkLights> povLight = builder.finish();
3224
3225 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3226 picture->cullRect().height(),
3227 kBGRA_8888_SkColorType,
3228 kOpaque_SkAlphaType);
3229
3230 // Create a new surface (that matches the backend of canvas)
3231 // to create the povDepthMap
3232 sk_sp<SkSurface> surf(this->makeSurface(info));
3233
3234 // Wrap another SPFCanvas around the surface
3235 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3236 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3237
3238 // set the depth map canvas to have the light as the user's POV
3239 depthMapCanvas->setLights(std::move(povLight));
3240
3241 depthMapCanvas->drawPicture(picture);
3242
3243 povDepthMap = surf->makeImageSnapshot();
3244 }
3245
3246 // diffuseMap
3247 {
3248 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3249 picture->cullRect().height(),
3250 kBGRA_8888_SkColorType,
3251 kOpaque_SkAlphaType);
3252
3253 sk_sp<SkSurface> surf(this->makeSurface(info));
3254 surf->getCanvas()->drawPicture(picture);
3255
3256 diffuseMap = surf->makeImageSnapshot();
3257 }
3258
3259 SkPaint shadowPaint;
3260
3261 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3262 SkShader::kClamp_TileMode);
3263
3264 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3265 SkShader::kClamp_TileMode);
3266
3267 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3268 std::move(diffuseShader),
3269 std::move(fLights),
3270 diffuseMap->width(),
3271 diffuseMap->height());
3272
3273 shadowPaint.setShader(shadowShader);
3274
3275 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003276}
3277#endif
3278
reed@android.com8a1c16f2008-12-17 15:59:43 +00003279///////////////////////////////////////////////////////////////////////////////
3280///////////////////////////////////////////////////////////////////////////////
3281
reed3aafe112016-08-18 12:45:34 -07003282SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003283 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003284
3285 SkASSERT(canvas);
3286
reed3aafe112016-08-18 12:45:34 -07003287 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003288 fDone = !fImpl->next();
3289}
3290
3291SkCanvas::LayerIter::~LayerIter() {
3292 fImpl->~SkDrawIter();
3293}
3294
3295void SkCanvas::LayerIter::next() {
3296 fDone = !fImpl->next();
3297}
3298
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003299SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003300 return fImpl->getDevice();
3301}
3302
3303const SkMatrix& SkCanvas::LayerIter::matrix() const {
3304 return fImpl->getMatrix();
3305}
3306
3307const SkPaint& SkCanvas::LayerIter::paint() const {
3308 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003309 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003310 paint = &fDefaultPaint;
3311 }
3312 return *paint;
3313}
3314
reed1e7f5e72016-04-27 07:49:17 -07003315const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003316int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3317int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003318
3319///////////////////////////////////////////////////////////////////////////////
3320
fmalitac3b589a2014-06-05 12:40:07 -07003321SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003322
3323///////////////////////////////////////////////////////////////////////////////
3324
3325static bool supported_for_raster_canvas(const SkImageInfo& info) {
3326 switch (info.alphaType()) {
3327 case kPremul_SkAlphaType:
3328 case kOpaque_SkAlphaType:
3329 break;
3330 default:
3331 return false;
3332 }
3333
3334 switch (info.colorType()) {
3335 case kAlpha_8_SkColorType:
3336 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003337 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003338 break;
3339 default:
3340 return false;
3341 }
3342
3343 return true;
3344}
3345
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003346SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3347 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003348 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003349 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003350
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003351 SkBitmap bitmap;
3352 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003353 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003354 }
halcanary385fe4d2015-08-26 13:07:48 -07003355 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003356}
reedd5fa1a42014-08-09 11:08:05 -07003357
3358///////////////////////////////////////////////////////////////////////////////
3359
3360SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003361 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003362 : fCanvas(canvas)
3363 , fSaveCount(canvas->getSaveCount())
3364{
bsalomon49f085d2014-09-05 13:34:00 -07003365 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003366 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003367 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003368 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003369 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003370 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003371 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003372 canvas->save();
3373 }
mtklein6cfa73a2014-08-13 13:33:49 -07003374
bsalomon49f085d2014-09-05 13:34:00 -07003375 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003376 canvas->concat(*matrix);
3377 }
3378}
3379
3380SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3381 fCanvas->restoreToCount(fSaveCount);
3382}
reede8f30622016-03-23 18:59:25 -07003383
3384#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3385SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3386 return this->makeSurface(info, props).release();
3387}
3388#endif