blob: 551ca60742b3a6e280306bf92b8998d1e84c3251 [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) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001447 SkMatrix m;
1448 m.setTranslate(dx, dy);
1449 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001452void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001453 SkMatrix m;
1454 m.setScale(sx, sy);
1455 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001458void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001459 SkMatrix m;
1460 m.setRotate(degrees);
1461 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
bungeman7438bfc2016-07-12 15:01:19 -07001464void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1465 SkMatrix m;
1466 m.setRotate(degrees, px, py);
1467 this->concat(m);
1468}
1469
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001470void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001471 SkMatrix m;
1472 m.setSkew(sx, sy);
1473 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001474}
1475
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001476void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001477 if (matrix.isIdentity()) {
1478 return;
1479 }
1480
reed2ff1fce2014-12-11 07:07:37 -08001481 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001483 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001484 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001485 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001486}
1487
reed8c30a812016-04-20 16:36:51 -07001488void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001490 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001491}
1492
1493void SkCanvas::setMatrix(const SkMatrix& matrix) {
1494 this->checkForDeferredSave();
1495 this->internalSetMatrix(matrix);
msarett9637ea92016-08-18 14:03:30 -07001496 fIsScaleTranslate = matrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001497 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498}
1499
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001501 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502}
1503
vjiaoblack95302da2016-07-21 10:25:54 -07001504#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001505void SkCanvas::translateZ(SkScalar z) {
1506 this->checkForDeferredSave();
1507 this->fMCRec->fCurDrawDepth += z;
1508 this->didTranslateZ(z);
1509}
1510
1511SkScalar SkCanvas::getZ() const {
1512 return this->fMCRec->fCurDrawDepth;
1513}
1514
vjiaoblack95302da2016-07-21 10:25:54 -07001515void SkCanvas::setLights(sk_sp<SkLights> lights) {
1516 this->fLights = lights;
1517}
1518
1519sk_sp<SkLights> SkCanvas::getLights() const {
1520 return this->fLights;
1521}
1522#endif
1523
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524//////////////////////////////////////////////////////////////////////////////
1525
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001526void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001527 if (!fAllowSoftClip) {
1528 doAA = false;
1529 }
1530
1531#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1532 // Check if we can quick-accept the clip call (and do nothing)
1533 //
reed74467162016-06-30 08:15:35 -07001534 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
halcanaryc5769b22016-08-10 07:13:21 -07001535 SkRect devR;
1536 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001537 // NOTE: this check is CTM specific, since we might round differently with a different
1538 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1539 // applied later (i.e. if this is going into a picture).
1540 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1541#if 0
1542 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1543 rect.left(), rect.top(), rect.right(), rect.bottom());
1544#endif
1545 return;
1546 }
1547 }
1548#endif
1549
reed2ff1fce2014-12-11 07:07:37 -08001550 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1552 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001553}
1554
1555void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001556#ifdef SK_ENABLE_CLIP_QUICKREJECT
1557 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001558 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001559 return;
reed@google.comda17f752012-08-16 18:27:05 +00001560 }
1561
reed@google.com3b3e8952012-08-16 20:53:31 +00001562 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001563 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001564 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001565
reed687fa1c2015-04-07 08:00:56 -07001566 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001567 (void)fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001568 fDeviceClipBounds.setEmpty();
reed2d1afab2016-06-29 14:33:11 -07001569 return;
reed@google.comda17f752012-08-16 18:27:05 +00001570 }
1571 }
1572#endif
1573
reed74467162016-06-30 08:15:35 -07001574 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001575 SkRect devR;
reed74467162016-06-30 08:15:35 -07001576 if (isScaleTrans) {
halcanaryc5769b22016-08-10 07:13:21 -07001577 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001578 }
bsalomonac8cabd2015-11-20 18:53:07 -08001579
reed2d1afab2016-06-29 14:33:11 -07001580#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001581 if (SkRegion::kIntersect_Op == op &&
1582 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001583 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001584 {
1585 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1586#if 0
1587 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1588 rect.left(), rect.top(), rect.right(), rect.bottom());
1589#endif
1590 return;
1591 }
1592 }
reed2d1afab2016-06-29 14:33:11 -07001593#endif
reedc64eff52015-11-21 12:39:45 -08001594
1595 AutoValidateClip avc(this);
1596
1597 fDeviceCMDirty = true;
reedc64eff52015-11-21 12:39:45 -08001598
reed74467162016-06-30 08:15:35 -07001599 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001600 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1601 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001602 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001604 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001605 // and clip against that, since it can handle any matrix. However, to
1606 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1607 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 SkPath path;
1609
1610 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001611 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612 }
msarettfbfa2582016-08-12 08:29:08 -07001613
1614 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615}
1616
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001617void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001618 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001619 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001620 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1622 } else {
1623 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001624 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001625}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001626
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001628 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001629 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001630 AutoValidateClip avc(this);
1631
1632 fDeviceCMDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001633 if (!fAllowSoftClip) {
1634 edgeStyle = kHard_ClipEdgeStyle;
1635 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001636
reed687fa1c2015-04-07 08:00:56 -07001637 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001638
senorblancoafc7cce2016-02-02 18:44:15 -08001639 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001640 kSoft_ClipEdgeStyle == edgeStyle);
msarettfbfa2582016-08-12 08:29:08 -07001641 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001642 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001643 }
1644
1645 SkPath path;
1646 path.addRRect(rrect);
1647 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001649}
1650
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001651void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001652 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001654
1655 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1656 SkRect r;
1657 if (path.isRect(&r)) {
1658 this->onClipRect(r, op, edgeStyle);
1659 return;
1660 }
1661 SkRRect rrect;
1662 if (path.isOval(&r)) {
1663 rrect.setOval(r);
1664 this->onClipRRect(rrect, op, edgeStyle);
1665 return;
1666 }
1667 if (path.isRRect(&rrect)) {
1668 this->onClipRRect(rrect, op, edgeStyle);
1669 return;
1670 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001671 }
robertphillips39f05382015-11-24 09:30:12 -08001672
1673 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001674}
1675
1676void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001677#ifdef SK_ENABLE_CLIP_QUICKREJECT
1678 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001679 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001680 return;
reed@google.comda17f752012-08-16 18:27:05 +00001681 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001682
reed@google.com3b3e8952012-08-16 20:53:31 +00001683 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001684 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001685 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001686
reed687fa1c2015-04-07 08:00:56 -07001687 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001688 (void)fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001689 fDeviceClipBounds.setEmpty();
reed2d1afab2016-06-29 14:33:11 -07001690 return;
reed@google.comda17f752012-08-16 18:27:05 +00001691 }
1692 }
1693#endif
1694
reed@google.com5c3d1472011-02-22 19:12:23 +00001695 AutoValidateClip avc(this);
1696
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697 fDeviceCMDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001698 if (!fAllowSoftClip) {
1699 edgeStyle = kHard_ClipEdgeStyle;
1700 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701
1702 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001703 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704
reed@google.comfe701122011-11-08 19:41:23 +00001705 // Check if the transfomation, or the original path itself
1706 // made us empty. Note this can also happen if we contained NaN
1707 // values. computing the bounds detects this, and will set our
1708 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1709 if (devPath.getBounds().isEmpty()) {
1710 // resetting the path will remove any NaN or other wanky values
1711 // that might upset our scan converter.
1712 devPath.reset();
1713 }
1714
reed@google.com5c3d1472011-02-22 19:12:23 +00001715 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001716 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001717
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001718 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001719 bool clipIsAA = getClipStack()->asPath(&devPath);
1720 if (clipIsAA) {
1721 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001722 }
fmalita1a481fe2015-02-04 07:39:34 -08001723
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001724 op = SkRegion::kReplace_Op;
1725 }
1726
senorblancoafc7cce2016-02-02 18:44:15 -08001727 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
msarettfbfa2582016-08-12 08:29:08 -07001728 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729}
1730
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001731void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001732 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001733 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001734}
1735
1736void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001737 AutoValidateClip avc(this);
1738
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740
reed@google.com5c3d1472011-02-22 19:12:23 +00001741 // todo: signal fClipStack that we have a region, and therefore (I guess)
1742 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001743 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001744
reed1f836ee2014-07-07 07:49:34 -07001745 fMCRec->fRasterClip.op(rgn, op);
msarettfbfa2582016-08-12 08:29:08 -07001746 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747}
1748
reed@google.com819c9212011-02-23 18:56:55 +00001749#ifdef SK_DEBUG
1750void SkCanvas::validateClip() const {
1751 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001752 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001753 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001754 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001755 return;
1756 }
1757
reed@google.com819c9212011-02-23 18:56:55 +00001758 SkIRect ir;
1759 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001760 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001761
reed687fa1c2015-04-07 08:00:56 -07001762 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001763 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001764 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001765 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001766 case SkClipStack::Element::kRect_Type:
1767 element->getRect().round(&ir);
1768 tmpClip.op(ir, element->getOp());
1769 break;
1770 case SkClipStack::Element::kEmpty_Type:
1771 tmpClip.setEmpty();
1772 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001773 default: {
1774 SkPath path;
1775 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001776 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001777 break;
1778 }
reed@google.com819c9212011-02-23 18:56:55 +00001779 }
1780 }
reed@google.com819c9212011-02-23 18:56:55 +00001781}
1782#endif
1783
reed@google.com90c07ea2012-04-13 13:50:27 +00001784void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001785 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001786 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001787
halcanary96fcdcc2015-08-27 07:41:13 -07001788 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001789 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001790 }
1791}
1792
reed@google.com5c3d1472011-02-22 19:12:23 +00001793///////////////////////////////////////////////////////////////////////////////
1794
reed@google.com754de5f2014-02-24 19:38:20 +00001795bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001796 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001797}
1798
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001799bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001800 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001801}
1802
msarettfbfa2582016-08-12 08:29:08 -07001803static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1804#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1805 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1806 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1807 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1808 return 0xF != _mm_movemask_ps(mask);
1809#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1810 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1811 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1812 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1813 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1814#else
1815 SkRect devRectAsRect;
1816 SkRect devClipAsRect;
1817 devRect.store(&devRectAsRect.fLeft);
1818 devClip.store(&devClipAsRect.fLeft);
1819 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1820#endif
1821}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001822
msarettfbfa2582016-08-12 08:29:08 -07001823// It's important for this function to not be inlined. Otherwise the compiler will share code
1824// between the fast path and the slow path, resulting in two slow paths.
1825static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1826 const SkMatrix& matrix) {
1827 SkRect deviceRect;
1828 matrix.mapRect(&deviceRect, src);
1829 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1830}
1831
1832bool SkCanvas::quickReject(const SkRect& src) const {
1833#ifdef SK_DEBUG
1834 // Verify that fDeviceClipBounds are set properly.
1835 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001836 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001837 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001838 } else {
msarettfbfa2582016-08-12 08:29:08 -07001839 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840 }
msarettfbfa2582016-08-12 08:29:08 -07001841
msarett9637ea92016-08-18 14:03:30 -07001842 // Verify that fIsScaleTranslate is set properly.
1843 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001844#endif
1845
msarett9637ea92016-08-18 14:03:30 -07001846 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001847 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1848 }
1849
1850 // We inline the implementation of mapScaleTranslate() for the fast path.
1851 float sx = fMCRec->fMatrix.getScaleX();
1852 float sy = fMCRec->fMatrix.getScaleY();
1853 float tx = fMCRec->fMatrix.getTranslateX();
1854 float ty = fMCRec->fMatrix.getTranslateY();
1855 Sk4f scale(sx, sy, sx, sy);
1856 Sk4f trans(tx, ty, tx, ty);
1857
1858 // Apply matrix.
1859 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1860
1861 // Make sure left < right, top < bottom.
1862 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1863 Sk4f min = Sk4f::Min(ltrb, rblt);
1864 Sk4f max = Sk4f::Max(ltrb, rblt);
1865 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1866 // ARM this sequence generates the fastest (a single instruction).
1867 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1868
1869 // Check if the device rect is NaN or outside the clip.
1870 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871}
1872
reed@google.com3b3e8952012-08-16 20:53:31 +00001873bool SkCanvas::quickReject(const SkPath& path) const {
1874 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875}
1876
reed@google.com3b3e8952012-08-16 20:53:31 +00001877bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001878 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001879 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880 return false;
1881 }
1882
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001883 SkMatrix inverse;
1884 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001885 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001886 if (bounds) {
1887 bounds->setEmpty();
1888 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001889 return false;
1890 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891
bsalomon49f085d2014-09-05 13:34:00 -07001892 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001893 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001894 // adjust it outwards in case we are antialiasing
1895 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001896
reed@google.com8f4d2302013-12-17 16:44:46 +00001897 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1898 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899 inverse.mapRect(bounds, r);
1900 }
1901 return true;
1902}
1903
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001904bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001905 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001906 if (clip.isEmpty()) {
1907 if (bounds) {
1908 bounds->setEmpty();
1909 }
1910 return false;
1911 }
1912
bsalomon49f085d2014-09-05 13:34:00 -07001913 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001914 *bounds = clip.getBounds();
1915 }
1916 return true;
1917}
1918
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001920 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921}
1922
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001923const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001924 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001925}
1926
robertphillips175dd9b2016-04-28 14:32:04 -07001927GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001928 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001929 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001930}
1931
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001932GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001933 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001934 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001935}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001936
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001937void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1938 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001939 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001940 if (outer.isEmpty()) {
1941 return;
1942 }
1943 if (inner.isEmpty()) {
1944 this->drawRRect(outer, paint);
1945 return;
1946 }
1947
1948 // We don't have this method (yet), but technically this is what we should
1949 // be able to assert...
1950 // SkASSERT(outer.contains(inner));
1951 //
1952 // For now at least check for containment of bounds
1953 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1954
1955 this->onDrawDRRect(outer, inner, paint);
1956}
1957
reed41af9662015-01-05 07:49:08 -08001958// These need to stop being virtual -- clients need to override the onDraw... versions
1959
1960void SkCanvas::drawPaint(const SkPaint& paint) {
1961 this->onDrawPaint(paint);
1962}
1963
1964void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1965 this->onDrawRect(r, paint);
1966}
1967
1968void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1969 this->onDrawOval(r, paint);
1970}
1971
1972void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1973 this->onDrawRRect(rrect, paint);
1974}
1975
1976void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1977 this->onDrawPoints(mode, count, pts, paint);
1978}
1979
1980void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1981 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1982 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1983 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1984 indices, indexCount, paint);
1985}
1986
1987void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1988 this->onDrawPath(path, paint);
1989}
1990
reeda85d4d02015-05-06 12:56:48 -07001991void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001992 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001993 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001994}
1995
reede47829b2015-08-06 10:02:53 -07001996void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1997 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001998 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001999 if (dst.isEmpty() || src.isEmpty()) {
2000 return;
2001 }
2002 this->onDrawImageRect(image, &src, dst, paint, constraint);
2003}
reed41af9662015-01-05 07:49:08 -08002004
reed84984ef2015-07-17 07:09:43 -07002005void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2006 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002007 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002008 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002009}
2010
reede47829b2015-08-06 10:02:53 -07002011void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
2012 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002013 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002014 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
2015 constraint);
2016}
reede47829b2015-08-06 10:02:53 -07002017
reed4c21dc52015-06-25 12:32:03 -07002018void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2019 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002020 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002021 if (dst.isEmpty()) {
2022 return;
2023 }
msarett552bca92016-08-03 06:53:26 -07002024 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
2025 this->onDrawImageNine(image, center, dst, paint);
2026 } else {
reede47829b2015-08-06 10:02:53 -07002027 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002028 }
reed4c21dc52015-06-25 12:32:03 -07002029}
2030
msarett16882062016-08-16 09:31:08 -07002031void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2032 const SkPaint* paint) {
2033 RETURN_ON_NULL(image);
2034 if (dst.isEmpty()) {
2035 return;
2036 }
2037 if (SkLatticeIter::Valid(image->width(), image->height(), lattice)) {
2038 this->onDrawImageLattice(image, lattice, dst, paint);
2039 } else {
2040 this->drawImageRect(image, dst, paint);
2041 }
2042}
2043
reed41af9662015-01-05 07:49:08 -08002044void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002045 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002046 return;
2047 }
reed41af9662015-01-05 07:49:08 -08002048 this->onDrawBitmap(bitmap, dx, dy, paint);
2049}
2050
reede47829b2015-08-06 10:02:53 -07002051void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002052 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002053 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002054 return;
2055 }
reede47829b2015-08-06 10:02:53 -07002056 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002057}
2058
reed84984ef2015-07-17 07:09:43 -07002059void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2060 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002061 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002062}
2063
reede47829b2015-08-06 10:02:53 -07002064void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2065 SrcRectConstraint constraint) {
2066 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2067 constraint);
2068}
reede47829b2015-08-06 10:02:53 -07002069
reed41af9662015-01-05 07:49:08 -08002070void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2071 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002072 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002073 return;
2074 }
msarett552bca92016-08-03 06:53:26 -07002075 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
2076 this->onDrawBitmapNine(bitmap, center, dst, paint);
2077 } else {
reeda5517e22015-07-14 10:54:12 -07002078 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002079 }
reed41af9662015-01-05 07:49:08 -08002080}
2081
msarettc573a402016-08-02 08:05:56 -07002082void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2083 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07002084 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002085 return;
2086 }
msarett16882062016-08-16 09:31:08 -07002087 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) {
2088 this->onDrawBitmapLattice(bitmap, lattice, dst, paint);
msarett552bca92016-08-03 06:53:26 -07002089 } else {
msarett16882062016-08-16 09:31:08 -07002090 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002091 }
msarettc573a402016-08-02 08:05:56 -07002092}
2093
reed71c3c762015-06-24 10:29:17 -07002094void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2095 const SkColor colors[], int count, SkXfermode::Mode mode,
2096 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002097 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002098 if (count <= 0) {
2099 return;
2100 }
2101 SkASSERT(atlas);
2102 SkASSERT(xform);
2103 SkASSERT(tex);
2104 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2105}
2106
reedf70b5312016-03-04 16:36:20 -08002107void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2108 if (key) {
2109 this->onDrawAnnotation(rect, key, value);
2110 }
2111}
2112
reede47829b2015-08-06 10:02:53 -07002113void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2114 const SkPaint* paint, SrcRectConstraint constraint) {
2115 if (src) {
2116 this->drawImageRect(image, *src, dst, paint, constraint);
2117 } else {
2118 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2119 dst, paint, constraint);
2120 }
2121}
2122void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2123 const SkPaint* paint, SrcRectConstraint constraint) {
2124 if (src) {
2125 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2126 } else {
2127 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2128 dst, paint, constraint);
2129 }
2130}
2131
tomhudsoncb3bd182016-05-18 07:24:16 -07002132void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2133 SkIRect layer_bounds = this->getTopLayerBounds();
2134 if (matrix) {
2135 *matrix = this->getTotalMatrix();
2136 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2137 }
2138 if (clip_bounds) {
2139 this->getClipDeviceBounds(clip_bounds);
2140 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2141 }
2142}
2143
reed@android.com8a1c16f2008-12-17 15:59:43 +00002144//////////////////////////////////////////////////////////////////////////////
2145// These are the virtual drawing methods
2146//////////////////////////////////////////////////////////////////////////////
2147
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002148void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002149 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002150 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2151 }
2152}
2153
reed41af9662015-01-05 07:49:08 -08002154void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002155 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002156 this->internalDrawPaint(paint);
2157}
2158
2159void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002160 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161
2162 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002163 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164 }
2165
reed@google.com4e2b3d32011-04-07 14:18:59 +00002166 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167}
2168
reed41af9662015-01-05 07:49:08 -08002169void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2170 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002171 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 if ((long)count <= 0) {
2173 return;
2174 }
2175
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002176 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002177 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002178 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002179 // special-case 2 points (common for drawing a single line)
2180 if (2 == count) {
2181 r.set(pts[0], pts[1]);
2182 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002183 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002184 }
senorblanco87e066e2015-10-28 11:23:36 -07002185 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2186 return;
2187 }
2188 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002189 }
reed@google.coma584aed2012-05-16 14:06:02 +00002190
halcanary96fcdcc2015-08-27 07:41:13 -07002191 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002193 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002194
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002196 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 }
reed@google.com4b226022011-01-11 18:32:13 +00002198
reed@google.com4e2b3d32011-04-07 14:18:59 +00002199 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200}
2201
reed41af9662015-01-05 07:49:08 -08002202void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002203 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002204 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002205 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002207 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2208 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2209 SkRect tmp(r);
2210 tmp.sort();
2211
senorblanco87e066e2015-10-28 11:23:36 -07002212 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2213 return;
2214 }
2215 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216 }
reed@google.com4b226022011-01-11 18:32:13 +00002217
reedc83a2972015-07-16 07:40:45 -07002218 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219
2220 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002221 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 }
2223
reed@google.com4e2b3d32011-04-07 14:18:59 +00002224 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225}
2226
reed41af9662015-01-05 07:49:08 -08002227void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002228 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002229 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002230 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002231 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002232 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2233 return;
2234 }
2235 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002236 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002237
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002238 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002239
2240 while (iter.next()) {
2241 iter.fDevice->drawOval(iter, oval, looper.paint());
2242 }
2243
2244 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002245}
2246
reed41af9662015-01-05 07:49:08 -08002247void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002248 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002249 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002250 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002251 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002252 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2253 return;
2254 }
2255 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002256 }
2257
2258 if (rrect.isRect()) {
2259 // call the non-virtual version
2260 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002261 return;
2262 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002263 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002264 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2265 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002266 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002267
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002268 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002269
2270 while (iter.next()) {
2271 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2272 }
2273
2274 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002275}
2276
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002277void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2278 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002279 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002280 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002281 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002282 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2283 return;
2284 }
2285 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002286 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002287
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002288 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002289
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002290 while (iter.next()) {
2291 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2292 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002293
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002294 LOOPER_END
2295}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002296
reed41af9662015-01-05 07:49:08 -08002297void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002298 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002299 if (!path.isFinite()) {
2300 return;
2301 }
2302
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002303 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002304 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002305 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002306 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002307 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2308 return;
2309 }
2310 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002312
2313 const SkRect& r = path.getBounds();
2314 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002315 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002316 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002317 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002318 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002319 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002321 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322
2323 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002324 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325 }
2326
reed@google.com4e2b3d32011-04-07 14:18:59 +00002327 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328}
2329
reed262a71b2015-12-05 13:07:27 -08002330bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002331 if (!paint.getImageFilter()) {
2332 return false;
2333 }
2334
2335 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002336 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002337 return false;
2338 }
2339
2340 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2341 // Once we can filter and the filter will return a result larger than itself, we should be
2342 // able to remove this constraint.
2343 // skbug.com/4526
2344 //
2345 SkPoint pt;
2346 ctm.mapXY(x, y, &pt);
2347 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2348 return ir.contains(fMCRec->fRasterClip.getBounds());
2349}
2350
reeda85d4d02015-05-06 12:56:48 -07002351void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002352 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002353 SkRect bounds = SkRect::MakeXYWH(x, y,
2354 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002355 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002356 SkRect tmp = bounds;
2357 if (paint) {
2358 paint->computeFastBounds(tmp, &tmp);
2359 }
2360 if (this->quickReject(tmp)) {
2361 return;
2362 }
reeda85d4d02015-05-06 12:56:48 -07002363 }
halcanary9d524f22016-03-29 09:03:52 -07002364
reeda85d4d02015-05-06 12:56:48 -07002365 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002366 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002367 paint = lazy.init();
2368 }
reed262a71b2015-12-05 13:07:27 -08002369
reeda2217ef2016-07-20 06:04:34 -07002370 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002371 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2372 *paint);
2373 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002374 special = this->getDevice()->makeSpecial(image);
2375 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002376 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002377 }
2378 }
2379
reed262a71b2015-12-05 13:07:27 -08002380 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2381
reeda85d4d02015-05-06 12:56:48 -07002382 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002383 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002384 if (special) {
2385 SkPoint pt;
2386 iter.fMatrix->mapXY(x, y, &pt);
2387 iter.fDevice->drawSpecial(iter, special.get(),
2388 SkScalarRoundToInt(pt.fX),
2389 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002390 } else {
2391 iter.fDevice->drawImage(iter, image, x, y, pnt);
2392 }
reeda85d4d02015-05-06 12:56:48 -07002393 }
halcanary9d524f22016-03-29 09:03:52 -07002394
reeda85d4d02015-05-06 12:56:48 -07002395 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002396}
2397
reed41af9662015-01-05 07:49:08 -08002398void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002399 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002400 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002401 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002402 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002403 if (paint) {
2404 paint->computeFastBounds(dst, &storage);
2405 }
2406 if (this->quickReject(storage)) {
2407 return;
2408 }
reeda85d4d02015-05-06 12:56:48 -07002409 }
2410 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002411 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002412 paint = lazy.init();
2413 }
halcanary9d524f22016-03-29 09:03:52 -07002414
senorblancoc41e7e12015-12-07 12:51:30 -08002415 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002416 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002417
reeda85d4d02015-05-06 12:56:48 -07002418 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002419 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002420 }
halcanary9d524f22016-03-29 09:03:52 -07002421
reeda85d4d02015-05-06 12:56:48 -07002422 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002423}
2424
reed41af9662015-01-05 07:49:08 -08002425void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002426 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002427 SkDEBUGCODE(bitmap.validate();)
2428
reed33366972015-10-08 09:22:02 -07002429 if (bitmap.drawsNothing()) {
2430 return;
2431 }
2432
2433 SkLazyPaint lazy;
2434 if (nullptr == paint) {
2435 paint = lazy.init();
2436 }
2437
2438 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2439
2440 SkRect storage;
2441 const SkRect* bounds = nullptr;
2442 if (paint->canComputeFastBounds()) {
2443 bitmap.getBounds(&storage);
2444 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002445 SkRect tmp = storage;
2446 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2447 return;
2448 }
2449 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450 }
reed@google.com4b226022011-01-11 18:32:13 +00002451
reeda2217ef2016-07-20 06:04:34 -07002452 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002453 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2454 *paint);
2455 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002456 special = this->getDevice()->makeSpecial(bitmap);
2457 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002458 drawAsSprite = false;
2459 }
2460 }
2461
reed262a71b2015-12-05 13:07:27 -08002462 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002463
2464 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002465 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002466 if (special) {
reed262a71b2015-12-05 13:07:27 -08002467 SkPoint pt;
2468 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002469 iter.fDevice->drawSpecial(iter, special.get(),
2470 SkScalarRoundToInt(pt.fX),
2471 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002472 } else {
2473 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2474 }
reed33366972015-10-08 09:22:02 -07002475 }
msarettfbfa2582016-08-12 08:29:08 -07002476
reed33366972015-10-08 09:22:02 -07002477 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478}
2479
reed@google.com9987ec32011-09-07 11:57:52 +00002480// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002481void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002482 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002483 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002484 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002485 return;
2486 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002487
halcanary96fcdcc2015-08-27 07:41:13 -07002488 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002489 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002490 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2491 return;
2492 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002493 }
reed@google.com3d608122011-11-21 15:16:16 +00002494
reed@google.com33535f32012-09-25 15:37:50 +00002495 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002496 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002497 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002498 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002499
senorblancoc41e7e12015-12-07 12:51:30 -08002500 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002501 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002502
reed@google.com33535f32012-09-25 15:37:50 +00002503 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002504 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002505 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002506
reed@google.com33535f32012-09-25 15:37:50 +00002507 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002508}
2509
reed41af9662015-01-05 07:49:08 -08002510void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002511 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002512 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002513 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002514 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002515}
2516
reed4c21dc52015-06-25 12:32:03 -07002517void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2518 const SkPaint* paint) {
2519 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002520
halcanary96fcdcc2015-08-27 07:41:13 -07002521 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002522 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002523 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2524 return;
2525 }
reed@google.com3d608122011-11-21 15:16:16 +00002526 }
halcanary9d524f22016-03-29 09:03:52 -07002527
reed4c21dc52015-06-25 12:32:03 -07002528 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002529 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002530 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002531 }
halcanary9d524f22016-03-29 09:03:52 -07002532
senorblancoc41e7e12015-12-07 12:51:30 -08002533 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002534
reed4c21dc52015-06-25 12:32:03 -07002535 while (iter.next()) {
2536 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002537 }
halcanary9d524f22016-03-29 09:03:52 -07002538
reed4c21dc52015-06-25 12:32:03 -07002539 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002540}
2541
reed41af9662015-01-05 07:49:08 -08002542void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2543 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002544 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002545 SkDEBUGCODE(bitmap.validate();)
2546
halcanary96fcdcc2015-08-27 07:41:13 -07002547 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002548 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002549 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2550 return;
2551 }
reed4c21dc52015-06-25 12:32:03 -07002552 }
halcanary9d524f22016-03-29 09:03:52 -07002553
reed4c21dc52015-06-25 12:32:03 -07002554 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002555 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002556 paint = lazy.init();
2557 }
halcanary9d524f22016-03-29 09:03:52 -07002558
senorblancoc41e7e12015-12-07 12:51:30 -08002559 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002560
reed4c21dc52015-06-25 12:32:03 -07002561 while (iter.next()) {
2562 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2563 }
halcanary9d524f22016-03-29 09:03:52 -07002564
reed4c21dc52015-06-25 12:32:03 -07002565 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002566}
2567
msarett16882062016-08-16 09:31:08 -07002568void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2569 const SkPaint* paint) {
2570 if (nullptr == paint || paint->canComputeFastBounds()) {
2571 SkRect storage;
2572 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2573 return;
2574 }
2575 }
2576
2577 SkLazyPaint lazy;
2578 if (nullptr == paint) {
2579 paint = lazy.init();
2580 }
2581
2582 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2583
2584 while (iter.next()) {
2585 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2586 }
2587
2588 LOOPER_END
2589}
2590
2591void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2592 const SkRect& dst, const SkPaint* paint) {
2593 if (nullptr == paint || paint->canComputeFastBounds()) {
2594 SkRect storage;
2595 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2596 return;
2597 }
2598 }
2599
2600 SkLazyPaint lazy;
2601 if (nullptr == paint) {
2602 paint = lazy.init();
2603 }
2604
2605 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2606
2607 while (iter.next()) {
2608 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2609 }
2610
2611 LOOPER_END
2612}
2613
reed@google.comf67e4cf2011-03-15 20:56:58 +00002614class SkDeviceFilteredPaint {
2615public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002616 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002617 uint32_t filteredFlags = device->filterTextFlags(paint);
2618 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002619 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002620 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002621 fPaint = newPaint;
2622 } else {
2623 fPaint = &paint;
2624 }
2625 }
2626
reed@google.comf67e4cf2011-03-15 20:56:58 +00002627 const SkPaint& paint() const { return *fPaint; }
2628
2629private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002630 const SkPaint* fPaint;
2631 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002632};
2633
bungeman@google.com52c748b2011-08-22 21:30:43 +00002634void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2635 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002636 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002637 draw.fDevice->drawRect(draw, r, paint);
2638 } else {
2639 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002640 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002641 draw.fDevice->drawRect(draw, r, p);
2642 }
2643}
2644
2645void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2646 const char text[], size_t byteLength,
2647 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002648 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002649
2650 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002651 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002652 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002653 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002654 return;
2655 }
2656
2657 SkScalar width = 0;
2658 SkPoint start;
2659
2660 start.set(0, 0); // to avoid warning
2661 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2662 SkPaint::kStrikeThruText_Flag)) {
2663 width = paint.measureText(text, byteLength);
2664
2665 SkScalar offsetX = 0;
2666 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2667 offsetX = SkScalarHalf(width);
2668 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2669 offsetX = width;
2670 }
2671 start.set(x - offsetX, y);
2672 }
2673
2674 if (0 == width) {
2675 return;
2676 }
2677
2678 uint32_t flags = paint.getFlags();
2679
2680 if (flags & (SkPaint::kUnderlineText_Flag |
2681 SkPaint::kStrikeThruText_Flag)) {
2682 SkScalar textSize = paint.getTextSize();
2683 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2684 SkRect r;
2685
2686 r.fLeft = start.fX;
2687 r.fRight = start.fX + width;
2688
2689 if (flags & SkPaint::kUnderlineText_Flag) {
2690 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2691 start.fY);
2692 r.fTop = offset;
2693 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002694 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002695 }
2696 if (flags & SkPaint::kStrikeThruText_Flag) {
2697 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2698 start.fY);
2699 r.fTop = offset;
2700 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002701 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002702 }
2703 }
2704}
2705
reed@google.come0d9ce82014-04-23 04:00:17 +00002706void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2707 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002708 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002709
2710 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002711 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002712 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002713 DrawTextDecorations(iter, dfp.paint(),
2714 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002715 }
2716
reed@google.com4e2b3d32011-04-07 14:18:59 +00002717 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002718}
2719
reed@google.come0d9ce82014-04-23 04:00:17 +00002720void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2721 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002722 SkPoint textOffset = SkPoint::Make(0, 0);
2723
halcanary96fcdcc2015-08-27 07:41:13 -07002724 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002725
reed@android.com8a1c16f2008-12-17 15:59:43 +00002726 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002727 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002728 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002729 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002730 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002731
reed@google.com4e2b3d32011-04-07 14:18:59 +00002732 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002733}
2734
reed@google.come0d9ce82014-04-23 04:00:17 +00002735void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2736 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002737
2738 SkPoint textOffset = SkPoint::Make(0, constY);
2739
halcanary96fcdcc2015-08-27 07:41:13 -07002740 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002741
reed@android.com8a1c16f2008-12-17 15:59:43 +00002742 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002743 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002744 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002745 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002746 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002747
reed@google.com4e2b3d32011-04-07 14:18:59 +00002748 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002749}
2750
reed@google.come0d9ce82014-04-23 04:00:17 +00002751void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2752 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002753 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002754
reed@android.com8a1c16f2008-12-17 15:59:43 +00002755 while (iter.next()) {
2756 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002757 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002758 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002759
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002760 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002761}
2762
reed45561a02016-07-07 12:47:17 -07002763void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2764 const SkRect* cullRect, const SkPaint& paint) {
2765 if (cullRect && this->quickReject(*cullRect)) {
2766 return;
2767 }
2768
2769 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2770
2771 while (iter.next()) {
2772 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2773 }
2774
2775 LOOPER_END
2776}
2777
fmalita00d5c2c2014-08-21 08:53:26 -07002778void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2779 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002780
fmalita85d5eb92015-03-04 11:20:12 -08002781 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002782 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002783 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002784 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002785 SkRect tmp;
2786 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2787 return;
2788 }
2789 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002790 }
2791
fmalita024f9962015-03-03 19:08:17 -08002792 // We cannot filter in the looper as we normally do, because the paint is
2793 // incomplete at this point (text-related attributes are embedded within blob run paints).
2794 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002795 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002796
fmalita85d5eb92015-03-04 11:20:12 -08002797 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002798
fmalitaaa1b9122014-08-28 14:32:24 -07002799 while (iter.next()) {
2800 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002801 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002802 }
2803
fmalitaaa1b9122014-08-28 14:32:24 -07002804 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002805
2806 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002807}
2808
reed@google.come0d9ce82014-04-23 04:00:17 +00002809// These will become non-virtual, so they always call the (virtual) onDraw... method
2810void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2811 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002812 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002813 if (byteLength) {
2814 this->onDrawText(text, byteLength, x, y, paint);
2815 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002816}
2817void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2818 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002819 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002820 if (byteLength) {
2821 this->onDrawPosText(text, byteLength, pos, paint);
2822 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002823}
2824void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2825 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002826 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002827 if (byteLength) {
2828 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2829 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002830}
2831void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2832 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002833 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002834 if (byteLength) {
2835 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2836 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002837}
reed45561a02016-07-07 12:47:17 -07002838void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2839 const SkRect* cullRect, const SkPaint& paint) {
2840 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2841 if (byteLength) {
2842 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2843 }
2844}
fmalita00d5c2c2014-08-21 08:53:26 -07002845void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2846 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002847 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002848 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002849 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002850}
reed@google.come0d9ce82014-04-23 04:00:17 +00002851
reed41af9662015-01-05 07:49:08 -08002852void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2853 const SkPoint verts[], const SkPoint texs[],
2854 const SkColor colors[], SkXfermode* xmode,
2855 const uint16_t indices[], int indexCount,
2856 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002857 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002858 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002859
reed@android.com8a1c16f2008-12-17 15:59:43 +00002860 while (iter.next()) {
2861 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002862 colors, xmode, indices, indexCount,
2863 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002864 }
reed@google.com4b226022011-01-11 18:32:13 +00002865
reed@google.com4e2b3d32011-04-07 14:18:59 +00002866 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867}
2868
dandovb3c9d1c2014-08-12 08:34:29 -07002869void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2870 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002871 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002872 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002873 return;
2874 }
mtklein6cfa73a2014-08-13 13:33:49 -07002875
dandovecfff212014-08-04 10:02:00 -07002876 // Since a patch is always within the convex hull of the control points, we discard it when its
2877 // bounding rectangle is completely outside the current clip.
2878 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002879 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002880 if (this->quickReject(bounds)) {
2881 return;
2882 }
mtklein6cfa73a2014-08-13 13:33:49 -07002883
dandovb3c9d1c2014-08-12 08:34:29 -07002884 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2885}
2886
2887void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2888 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2889
halcanary96fcdcc2015-08-27 07:41:13 -07002890 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002891
dandovecfff212014-08-04 10:02:00 -07002892 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002893 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002894 }
mtklein6cfa73a2014-08-13 13:33:49 -07002895
dandovecfff212014-08-04 10:02:00 -07002896 LOOPER_END
2897}
2898
reeda8db7282015-07-07 10:22:31 -07002899void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002900 RETURN_ON_NULL(dr);
2901 if (x || y) {
2902 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2903 this->onDrawDrawable(dr, &matrix);
2904 } else {
2905 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002906 }
2907}
2908
reeda8db7282015-07-07 10:22:31 -07002909void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002910 RETURN_ON_NULL(dr);
2911 if (matrix && matrix->isIdentity()) {
2912 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002913 }
reede3b38ce2016-01-08 09:18:44 -08002914 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002915}
2916
2917void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2918 SkRect bounds = dr->getBounds();
2919 if (matrix) {
2920 matrix->mapRect(&bounds);
2921 }
2922 if (this->quickReject(bounds)) {
2923 return;
2924 }
2925 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002926}
2927
reed71c3c762015-06-24 10:29:17 -07002928void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2929 const SkColor colors[], int count, SkXfermode::Mode mode,
2930 const SkRect* cull, const SkPaint* paint) {
2931 if (cull && this->quickReject(*cull)) {
2932 return;
2933 }
2934
2935 SkPaint pnt;
2936 if (paint) {
2937 pnt = *paint;
2938 }
halcanary9d524f22016-03-29 09:03:52 -07002939
halcanary96fcdcc2015-08-27 07:41:13 -07002940 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002941 while (iter.next()) {
2942 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2943 }
2944 LOOPER_END
2945}
2946
reedf70b5312016-03-04 16:36:20 -08002947void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2948 SkASSERT(key);
2949
2950 SkPaint paint;
2951 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2952 while (iter.next()) {
2953 iter.fDevice->drawAnnotation(iter, rect, key, value);
2954 }
2955 LOOPER_END
2956}
2957
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958//////////////////////////////////////////////////////////////////////////////
2959// These methods are NOT virtual, and therefore must call back into virtual
2960// methods, rather than actually drawing themselves.
2961//////////////////////////////////////////////////////////////////////////////
2962
2963void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002964 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002965 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002966 SkPaint paint;
2967
2968 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002969 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002970 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002971 }
2972 this->drawPaint(paint);
2973}
2974
reed@android.com845fdac2009-06-23 03:01:32 +00002975void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002976 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977 SkPaint paint;
2978
2979 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002980 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002981 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002982 }
2983 this->drawPaint(paint);
2984}
2985
2986void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002987 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002988 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002989
reed@android.com8a1c16f2008-12-17 15:59:43 +00002990 pt.set(x, y);
2991 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2992}
2993
2994void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002995 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002996 SkPoint pt;
2997 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002998
reed@android.com8a1c16f2008-12-17 15:59:43 +00002999 pt.set(x, y);
3000 paint.setColor(color);
3001 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3002}
3003
3004void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
3005 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003006 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003007 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00003008
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009 pts[0].set(x0, y0);
3010 pts[1].set(x1, y1);
3011 this->drawPoints(kLines_PointMode, 2, pts, paint);
3012}
3013
3014void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
3015 SkScalar right, SkScalar bottom,
3016 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003017 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003018 SkRect r;
3019
3020 r.set(left, top, right, bottom);
3021 this->drawRect(r, paint);
3022}
3023
3024void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
3025 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003026 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003027 if (radius < 0) {
3028 radius = 0;
3029 }
3030
3031 SkRect r;
3032 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003033 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003034}
3035
3036void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3037 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003038 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003039 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003040 SkRRect rrect;
3041 rrect.setRectXY(r, rx, ry);
3042 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003043 } else {
3044 this->drawRect(r, paint);
3045 }
3046}
3047
reed@android.com8a1c16f2008-12-17 15:59:43 +00003048void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3049 SkScalar sweepAngle, bool useCenter,
3050 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003051 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003052 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
3053 this->drawOval(oval, paint);
3054 } else {
3055 SkPath path;
3056 if (useCenter) {
3057 path.moveTo(oval.centerX(), oval.centerY());
3058 }
3059 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
3060 if (useCenter) {
3061 path.close();
3062 }
3063 this->drawPath(path, paint);
3064 }
3065}
3066
3067void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3068 const SkPath& path, SkScalar hOffset,
3069 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003070 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003071 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003072
reed@android.com8a1c16f2008-12-17 15:59:43 +00003073 matrix.setTranslate(hOffset, vOffset);
3074 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3075}
3076
reed@android.comf76bacf2009-05-13 14:00:33 +00003077///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003078
3079/**
3080 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3081 * against the playback cost of recursing into the subpicture to get at its actual ops.
3082 *
3083 * For now we pick a conservatively small value, though measurement (and other heuristics like
3084 * the type of ops contained) may justify changing this value.
3085 */
3086#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003087
reedd5fa1a42014-08-09 11:08:05 -07003088void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003089 RETURN_ON_NULL(picture);
3090
reed1c2c4412015-04-30 13:09:24 -07003091 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003092 if (matrix && matrix->isIdentity()) {
3093 matrix = nullptr;
3094 }
3095 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3096 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3097 picture->playback(this);
3098 } else {
3099 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003100 }
3101}
robertphillips9b14f262014-06-04 05:40:44 -07003102
reedd5fa1a42014-08-09 11:08:05 -07003103void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3104 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003105 if (!paint || paint->canComputeFastBounds()) {
3106 SkRect bounds = picture->cullRect();
3107 if (paint) {
3108 paint->computeFastBounds(bounds, &bounds);
3109 }
3110 if (matrix) {
3111 matrix->mapRect(&bounds);
3112 }
3113 if (this->quickReject(bounds)) {
3114 return;
3115 }
3116 }
3117
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003118 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003119 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003120}
3121
vjiaoblack95302da2016-07-21 10:25:54 -07003122#ifdef SK_EXPERIMENTAL_SHADOWING
3123void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3124 const SkMatrix* matrix,
3125 const SkPaint* paint) {
3126 RETURN_ON_NULL(picture);
3127
3128 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3129
3130 this->onDrawShadowedPicture(picture, matrix, paint);
3131}
3132
3133void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3134 const SkMatrix* matrix,
3135 const SkPaint* paint) {
vjiaoblack904527d2016-08-09 09:32:09 -07003136 if (!paint || paint->canComputeFastBounds()) {
3137 SkRect bounds = picture->cullRect();
3138 if (paint) {
3139 paint->computeFastBounds(bounds, &bounds);
3140 }
3141 if (matrix) {
3142 matrix->mapRect(&bounds);
3143 }
3144 if (this->quickReject(bounds)) {
3145 return;
3146 }
3147 }
3148
3149 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3150
3151 for (int i = 0; i < fLights->numLights(); ++i) {
3152 // skip over ambient lights; they don't cast shadows
3153 // lights that have shadow maps do not need updating (because lights are immutable)
3154
3155 if (SkLights::Light::kAmbient_LightType == fLights->light(i).type() ||
3156 fLights->light(i).getShadowMap() != nullptr) {
3157 continue;
3158 }
3159
3160 // TODO: compute the correct size of the depth map from the light properties
3161 // TODO: maybe add a kDepth_8_SkColorType
3162 // TODO: find actual max depth of picture
3163 SkISize shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3164 fLights->light(i), 255,
3165 picture->cullRect().width(),
3166 picture->cullRect().height());
3167
3168 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3169 kBGRA_8888_SkColorType,
3170 kOpaque_SkAlphaType);
3171
3172 // Create a new surface (that matches the backend of canvas)
3173 // for each shadow map
3174 sk_sp<SkSurface> surf(this->makeSurface(info));
3175
3176 // Wrap another SPFCanvas around the surface
3177 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3178 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3179
3180 // set the depth map canvas to have the light we're drawing.
3181 SkLights::Builder builder;
3182 builder.add(fLights->light(i));
3183 sk_sp<SkLights> curLight = builder.finish();
3184
3185 depthMapCanvas->setLights(std::move(curLight));
3186 depthMapCanvas->drawPicture(picture);
3187
3188 fLights->light(i).setShadowMap(surf->makeImageSnapshot());
3189 }
3190
3191 sk_sp<SkImage> povDepthMap;
3192 sk_sp<SkImage> diffuseMap;
3193
3194 // TODO: pass the depth to the shader in vertices, or uniforms
3195 // so we don't have to render depth and color separately
3196
3197 // povDepthMap
3198 {
3199 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003200 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3201 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003202 sk_sp<SkLights> povLight = builder.finish();
3203
3204 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3205 picture->cullRect().height(),
3206 kBGRA_8888_SkColorType,
3207 kOpaque_SkAlphaType);
3208
3209 // Create a new surface (that matches the backend of canvas)
3210 // to create the povDepthMap
3211 sk_sp<SkSurface> surf(this->makeSurface(info));
3212
3213 // Wrap another SPFCanvas around the surface
3214 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3215 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3216
3217 // set the depth map canvas to have the light as the user's POV
3218 depthMapCanvas->setLights(std::move(povLight));
3219
3220 depthMapCanvas->drawPicture(picture);
3221
3222 povDepthMap = surf->makeImageSnapshot();
3223 }
3224
3225 // diffuseMap
3226 {
3227 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3228 picture->cullRect().height(),
3229 kBGRA_8888_SkColorType,
3230 kOpaque_SkAlphaType);
3231
3232 sk_sp<SkSurface> surf(this->makeSurface(info));
3233 surf->getCanvas()->drawPicture(picture);
3234
3235 diffuseMap = surf->makeImageSnapshot();
3236 }
3237
3238 SkPaint shadowPaint;
3239
3240 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3241 SkShader::kClamp_TileMode);
3242
3243 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3244 SkShader::kClamp_TileMode);
3245
3246 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3247 std::move(diffuseShader),
3248 std::move(fLights),
3249 diffuseMap->width(),
3250 diffuseMap->height());
3251
3252 shadowPaint.setShader(shadowShader);
3253
3254 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003255}
3256#endif
3257
reed@android.com8a1c16f2008-12-17 15:59:43 +00003258///////////////////////////////////////////////////////////////////////////////
3259///////////////////////////////////////////////////////////////////////////////
3260
reed3aafe112016-08-18 12:45:34 -07003261SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003262 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003263
3264 SkASSERT(canvas);
3265
reed3aafe112016-08-18 12:45:34 -07003266 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003267 fDone = !fImpl->next();
3268}
3269
3270SkCanvas::LayerIter::~LayerIter() {
3271 fImpl->~SkDrawIter();
3272}
3273
3274void SkCanvas::LayerIter::next() {
3275 fDone = !fImpl->next();
3276}
3277
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003278SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003279 return fImpl->getDevice();
3280}
3281
3282const SkMatrix& SkCanvas::LayerIter::matrix() const {
3283 return fImpl->getMatrix();
3284}
3285
3286const SkPaint& SkCanvas::LayerIter::paint() const {
3287 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003288 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003289 paint = &fDefaultPaint;
3290 }
3291 return *paint;
3292}
3293
reed1e7f5e72016-04-27 07:49:17 -07003294const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003295int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3296int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003297
3298///////////////////////////////////////////////////////////////////////////////
3299
fmalitac3b589a2014-06-05 12:40:07 -07003300SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003301
3302///////////////////////////////////////////////////////////////////////////////
3303
3304static bool supported_for_raster_canvas(const SkImageInfo& info) {
3305 switch (info.alphaType()) {
3306 case kPremul_SkAlphaType:
3307 case kOpaque_SkAlphaType:
3308 break;
3309 default:
3310 return false;
3311 }
3312
3313 switch (info.colorType()) {
3314 case kAlpha_8_SkColorType:
3315 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003316 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003317 break;
3318 default:
3319 return false;
3320 }
3321
3322 return true;
3323}
3324
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003325SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3326 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003327 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003328 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003329
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003330 SkBitmap bitmap;
3331 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003332 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003333 }
halcanary385fe4d2015-08-26 13:07:48 -07003334 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003335}
reedd5fa1a42014-08-09 11:08:05 -07003336
3337///////////////////////////////////////////////////////////////////////////////
3338
3339SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003340 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003341 : fCanvas(canvas)
3342 , fSaveCount(canvas->getSaveCount())
3343{
bsalomon49f085d2014-09-05 13:34:00 -07003344 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003345 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003346 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003347 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003348 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003349 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003350 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003351 canvas->save();
3352 }
mtklein6cfa73a2014-08-13 13:33:49 -07003353
bsalomon49f085d2014-09-05 13:34:00 -07003354 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003355 canvas->concat(*matrix);
3356 }
3357}
3358
3359SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3360 fCanvas->restoreToCount(fSaveCount);
3361}
reede8f30622016-03-23 18:59:25 -07003362
3363#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3364SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3365 return this->makeSurface(info, props).release();
3366}
3367#endif