blob: e9cf7e3b290e2ccbe64d0d0fc9f6921ff02929ff [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/private/SkNx.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040023#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050025#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050035#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050036#include "src/core/SkMatrixUtils.h"
37#include "src/core/SkPaintPriv.h"
38#include "src/core/SkRasterClip.h"
39#include "src/core/SkSpecialImage.h"
40#include "src/core/SkStrikeCache.h"
41#include "src/core/SkTLazy.h"
42#include "src/core/SkTextFormatParams.h"
43#include "src/core/SkTraceEvent.h"
44#include "src/image/SkImage_Base.h"
45#include "src/image/SkSurface_Base.h"
46#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040047
bungemand3ebb482015-08-05 13:57:49 -070048#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000050#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050051#include "include/gpu/GrContext.h"
52#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000053#endif
54
reede3b38ce2016-01-08 09:18:44 -080055#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050056#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080057
Mike Klein1bb7e232019-12-10 08:58:52 -060058// This is a test: static_assert with no message is a c++17 feature,
59// and std::max() is constexpr only since the c++14 stdlib.
60static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000061
Mike Reed139e5e02017-03-08 11:29:33 -050062///////////////////////////////////////////////////////////////////////////////////////////////////
63
reedc83a2972015-07-16 07:40:45 -070064/*
65 * Return true if the drawing this rect would hit every pixels in the canvas.
66 *
67 * Returns false if
68 * - rect does not contain the canvas' bounds
69 * - paint is not fill
70 * - paint would blur or otherwise change the coverage of the rect
71 */
72bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
73 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070074 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
75 (int)kNone_ShaderOverrideOpacity,
76 "need_matching_enums0");
77 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
78 (int)kOpaque_ShaderOverrideOpacity,
79 "need_matching_enums1");
80 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
81 (int)kNotOpaque_ShaderOverrideOpacity,
82 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070083
84 const SkISize size = this->getBaseLayerSize();
85 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050086
87 // if we're clipped at all, we can't overwrite the entire surface
88 {
89 SkBaseDevice* base = this->getDevice();
90 SkBaseDevice* top = this->getTopDevice();
91 if (base != top) {
92 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
93 }
94 if (!base->clipIsWideOpen()) {
95 return false;
96 }
reedc83a2972015-07-16 07:40:45 -070097 }
98
99 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700100 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700101 return false; // conservative
102 }
halcanaryc5769b22016-08-10 07:13:21 -0700103
104 SkRect devRect;
105 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
106 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700107 return false;
108 }
109 }
110
111 if (paint) {
112 SkPaint::Style paintStyle = paint->getStyle();
113 if (!(paintStyle == SkPaint::kFill_Style ||
114 paintStyle == SkPaint::kStrokeAndFill_Style)) {
115 return false;
116 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400117 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700118 return false; // conservative
119 }
120 }
121 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
122}
123
124///////////////////////////////////////////////////////////////////////////////////////////////////
125
reed@google.comda17f752012-08-16 18:27:05 +0000126// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127//#define SK_TRACE_SAVERESTORE
128
129#ifdef SK_TRACE_SAVERESTORE
130 static int gLayerCounter;
131 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
132 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
133
134 static int gRecCounter;
135 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
136 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
137
138 static int gCanvasCounter;
139 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
140 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
141#else
142 #define inc_layer()
143 #define dec_layer()
144 #define inc_rec()
145 #define dec_rec()
146 #define inc_canvas()
147 #define dec_canvas()
148#endif
149
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000150typedef SkTLazy<SkPaint> SkLazyPaint;
151
reedc83a2972015-07-16 07:40:45 -0700152void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000153 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700154 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
155 ? SkSurface::kDiscard_ContentChangeMode
156 : SkSurface::kRetain_ContentChangeMode);
157 }
158}
159
160void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
161 ShaderOverrideOpacity overrideOpacity) {
162 if (fSurfaceBase) {
163 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
164 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
165 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
166 // and therefore we don't care which mode we're in.
167 //
168 if (fSurfaceBase->outstandingImageSnapshot()) {
169 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
170 mode = SkSurface::kDiscard_ContentChangeMode;
171 }
172 }
173 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000174 }
175}
176
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000179/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 The clip/matrix/proc are fields that reflect the top of the save/restore
181 stack. Whenever the canvas changes, it marks a dirty flag, and then before
182 these are used (assuming we're not on a layer) we rebuild these cache
183 values: they reflect the top of the save stack, but translated and clipped
184 by the device's XY offset and bitmap-bounds.
185*/
186struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400187 DeviceCM* fNext;
188 sk_sp<SkBaseDevice> fDevice;
189 SkRasterClip fClip;
190 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
191 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400192 sk_sp<SkImage> fClipImage;
193 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
Florin Malita53f77bd2017-04-28 13:48:37 -0400195 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000196 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700197 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400198 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500199 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700200 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000201 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400202 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400203 {}
reed@google.com4b226022011-01-11 18:32:13 +0000204
mtkleinfeaadee2015-04-08 11:25:48 -0700205 void reset(const SkIRect& bounds) {
206 SkASSERT(!fPaint);
207 SkASSERT(!fNext);
208 SkASSERT(fDevice);
209 fClip.setRect(bounds);
210 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211};
212
Mike Reed148b7fd2018-12-18 17:38:18 -0500213namespace {
214// Encapsulate state needed to restore from saveBehind()
215struct BackImage {
216 sk_sp<SkSpecialImage> fImage;
217 SkIPoint fLoc;
218};
219}
220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221/* This is the record we keep for each save/restore level in the stack.
222 Since a level optionally copies the matrix and/or stack, we have pointers
223 for these fields. If the value is copied for this level, the copy is
224 stored in the ...Storage field, and the pointer points to that. If the
225 value is not copied for this level, we ignore ...Storage, and just point
226 at the corresponding value in the previous level in the stack.
227*/
228class SkCanvas::MCRec {
229public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500230 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 /* If there are any layers in the stack, this points to the top-most
232 one that is at or below this level in the stack (so we know what
233 bitmap/device to draw into from this level. This value is NOT
234 reference counted, since the real owner is either our fLayer field,
235 or a previous one in a lower level.)
236 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500237 DeviceCM* fTopLayer;
238 std::unique_ptr<BackImage> fBackImage;
239 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500240 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500241 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
Mike Reeda1361362017-03-07 09:37:29 -0500243 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700244 fLayer = nullptr;
245 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800246 fMatrix.reset();
247 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700248
reedd9544982014-09-09 18:46:22 -0700249 // don't bother initializing fNext
250 inc_rec();
251 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400252 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700253 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700254 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800255 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 // don't bother initializing fNext
258 inc_rec();
259 }
260 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700261 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 dec_rec();
263 }
mtkleinfeaadee2015-04-08 11:25:48 -0700264
265 void reset(const SkIRect& bounds) {
266 SkASSERT(fLayer);
267 SkASSERT(fDeferredSaveCount == 0);
268
269 fMatrix.reset();
270 fRasterClip.setRect(bounds);
271 fLayer->reset(bounds);
272 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
Mike Reeda1361362017-03-07 09:37:29 -0500275class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276public:
Mike Reeda1361362017-03-07 09:37:29 -0500277 SkDrawIter(SkCanvas* canvas)
278 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
279 {}
reed@google.com4b226022011-01-11 18:32:13 +0000280
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000282 const DeviceCM* rec = fCurrLayer;
283 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400284 fDevice = rec->fDevice.get();
285 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700287 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 return true;
289 }
290 return false;
291 }
reed@google.com4b226022011-01-11 18:32:13 +0000292
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000294
Mike Reed99330ba2017-02-22 11:01:08 -0500295 SkBaseDevice* fDevice;
296
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 const DeviceCM* fCurrLayer;
299 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300};
301
Florin Malita713b8ef2017-04-28 10:57:24 -0400302#define FOR_EACH_TOP_DEVICE( code ) \
303 do { \
304 DeviceCM* layer = fMCRec->fTopLayer; \
305 while (layer) { \
306 SkBaseDevice* device = layer->fDevice.get(); \
307 if (device) { \
308 code; \
309 } \
310 layer = layer->fNext; \
311 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500312 } while (0)
313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314/////////////////////////////////////////////////////////////////////////////
315
reeddbc3cef2015-04-29 12:18:57 -0700316/**
317 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700318 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700319 */
reedd053ce92016-03-22 10:17:23 -0700320static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700321 SkImageFilter* imgf = paint.getImageFilter();
322 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700323 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700324 }
325
reedd053ce92016-03-22 10:17:23 -0700326 SkColorFilter* imgCFPtr;
327 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700328 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700329 }
reedd053ce92016-03-22 10:17:23 -0700330 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700331
332 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700333 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700334 // there is no existing paint colorfilter, so we can just return the imagefilter's
335 return imgCF;
336 }
337
338 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
339 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500340 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700341}
342
senorblanco87e066e2015-10-28 11:23:36 -0700343/**
344 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
345 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
346 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
347 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
348 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
349 * conservative "effective" bounds based on the settings in the paint... with one exception. This
350 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
351 * deliberately ignored.
352 */
353static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
354 const SkRect& rawBounds,
355 SkRect* storage) {
356 SkPaint tmpUnfiltered(paint);
357 tmpUnfiltered.setImageFilter(nullptr);
358 if (tmpUnfiltered.canComputeFastBounds()) {
359 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
360 } else {
361 return rawBounds;
362 }
363}
364
Mike Reed38992392019-07-30 10:48:15 -0400365class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366public:
senorblanco87e066e2015-10-28 11:23:36 -0700367 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
368 // paint. It's used to determine the size of the offscreen layer for filters.
369 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400370 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
371 bool skipLayerForImageFilter = false,
372 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400374 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700376 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
Mike Reed38992392019-07-30 10:48:15 -0400378 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
379 SkASSERT(!fLazyPaint.isValid());
380 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700381 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700382 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700383 fPaint = paint;
384 }
385
386 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700387 /**
388 * We implement ImageFilters for a given draw by creating a layer, then applying the
389 * imagefilter to the pixels of that layer (its backing surface/image), and then
390 * we call restore() to xfer that layer to the main canvas.
391 *
392 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
393 * 2. Generate the src pixels:
394 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
395 * return (fPaint). We then draw the primitive (using srcover) into a cleared
396 * buffer/surface.
397 * 3. Restore the layer created in #1
398 * The imagefilter is passed the buffer/surface from the layer (now filled with the
399 * src pixels of the primitive). It returns a new "filtered" buffer, which we
400 * draw onto the previous layer using the xfermode from the original paint.
401 */
Mike Reed38992392019-07-30 10:48:15 -0400402
403 SkPaint restorePaint;
404 restorePaint.setImageFilter(fPaint->refImageFilter());
405 restorePaint.setBlendMode(fPaint->getBlendMode());
406
senorblanco87e066e2015-10-28 11:23:36 -0700407 SkRect storage;
408 if (rawBounds) {
409 // Make rawBounds include all paint outsets except for those due to image filters.
410 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
411 }
Mike Reed38992392019-07-30 10:48:15 -0400412 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700413 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700414 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000415
Mike Reed38992392019-07-30 10:48:15 -0400416 // Remove the restorePaint fields from our "working" paint
417 SkASSERT(!fLazyPaint.isValid());
418 SkPaint* paint = fLazyPaint.set(origPaint);
419 paint->setImageFilter(nullptr);
420 paint->setBlendMode(SkBlendMode::kSrcOver);
421 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000422 }
423 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000424
Mike Reed38992392019-07-30 10:48:15 -0400425 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700426 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000427 fCanvas->internalRestore();
428 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000429 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000431
reed@google.com4e2b3d32011-04-07 14:18:59 +0000432 const SkPaint& paint() const {
433 SkASSERT(fPaint);
434 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437private:
Mike Reed38992392019-07-30 10:48:15 -0400438 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000439 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400440 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700442 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443};
444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445////////// macros to place around the internal draw calls //////////////////
446
Mike Reed38992392019-07-30 10:48:15 -0400447#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700448 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400449 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
450 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800451
452
Mike Reed38992392019-07-30 10:48:15 -0400453#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000454 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400455 AutoLayerForImageFilter draw(this, paint, true); \
456 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000457
Mike Reed38992392019-07-30 10:48:15 -0400458#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000459 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400460 AutoLayerForImageFilter draw(this, paint, false, bounds); \
461 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000462
Mike Reed38992392019-07-30 10:48:15 -0400463#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700464 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400465 AutoLayerForImageFilter draw(this, paint, false, bounds); \
466 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700467
Mike Reed38992392019-07-30 10:48:15 -0400468#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469
470////////////////////////////////////////////////////////////////////////////
471
msarettfbfa2582016-08-12 08:29:08 -0700472static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
473 if (bounds.isEmpty()) {
474 return SkRect::MakeEmpty();
475 }
476
477 // Expand bounds out by 1 in case we are anti-aliasing. We store the
478 // bounds as floats to enable a faster quick reject implementation.
479 SkRect dst;
480 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
481 return dst;
482}
483
mtkleinfeaadee2015-04-08 11:25:48 -0700484void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
485 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700486 fMCRec->reset(bounds);
487
488 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500489 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400490 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700491 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700492 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700493}
494
Hal Canary363a3f82018-10-04 11:04:48 -0400495void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800496 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497
498 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500499 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500500 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700501 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
reeda499f902015-05-01 09:34:31 -0700503 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
504 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400505 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508
halcanary96fcdcc2015-08-27 07:41:13 -0700509 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000510
reedf92c8662014-08-18 08:02:43 -0700511 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700512 // The root device and the canvas should always have the same pixel geometry
513 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800514 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700515 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500516
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700518 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400519
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500520 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521}
522
reed@google.comcde92112011-07-06 20:00:52 +0000523SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000524 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700525 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000527 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000528
Hal Canary363a3f82018-10-04 11:04:48 -0400529 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000530}
531
reed96a857e2015-01-25 10:33:58 -0800532SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800534 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535{
536 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400537 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500538 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700539}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000540
Hal Canary363a3f82018-10-04 11:04:48 -0400541SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700542 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700543 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700544{
545 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700546
Mike Reed566e53c2017-03-10 10:49:45 -0500547 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400548 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700549}
550
Herb Derbyefe39bc2018-05-01 17:06:20 -0400551SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000552 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700553 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700556
Hal Canary363a3f82018-10-04 11:04:48 -0400557 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700558}
559
reed4a8126e2014-09-22 07:29:03 -0700560SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700561 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700562 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700563{
564 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700565
Mike Reed910ca0f2018-04-25 13:04:05 -0400566 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400567 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700568}
reed29c857d2014-09-21 10:25:07 -0700569
Mike Reed356f7c22017-01-10 11:58:39 -0500570SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
571 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700572 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
573 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500574 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700575{
576 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700577
Mike Reed910ca0f2018-04-25 13:04:05 -0400578 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400579 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580}
581
Mike Reed356f7c22017-01-10 11:58:39 -0500582SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
583
Matt Sarett31f99ce2017-04-11 08:46:01 -0400584#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
585SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
586 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
587 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
588 , fAllocator(nullptr)
589{
590 inc_canvas();
591
592 SkBitmap tmp(bitmap);
593 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400594 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400595 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400596}
597#endif
598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599SkCanvas::~SkCanvas() {
600 // free up the contents of our deque
601 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 this->internalRestore(); // restore the last, since we're going away
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 dec_canvas();
606}
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608///////////////////////////////////////////////////////////////////////////////
609
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000610void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700611 this->onFlush();
612}
613
614void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000615 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000616 if (device) {
617 device->flush();
618 }
619}
620
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500621SkSurface* SkCanvas::getSurface() const {
622 return fSurfaceBase;
623}
624
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000625SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000626 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000627 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
628}
629
senorblancoafc7cce2016-02-02 18:44:15 -0800630SkIRect SkCanvas::getTopLayerBounds() const {
631 SkBaseDevice* d = this->getTopDevice();
632 if (!d) {
633 return SkIRect::MakeEmpty();
634 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500635 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800636}
637
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000638SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000640 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400642 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643}
644
Florin Malita0ed3b642017-01-13 16:56:38 +0000645SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400646 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000647}
648
Mike Reed353196f2017-07-21 11:01:18 -0400649bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000650 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400651 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000652}
653
Mike Reed353196f2017-07-21 11:01:18 -0400654bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
655 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400656}
657
658bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
659 SkPixmap pm;
660 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
661}
662
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000663bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400664 SkPixmap pm;
665 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700666 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000667 }
668 return false;
669}
670
Matt Sarett03dd6d52017-01-23 12:15:09 -0500671bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000672 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 SkBaseDevice* device = this->getDevice();
674 if (!device) {
675 return false;
676 }
677
Matt Sarett03dd6d52017-01-23 12:15:09 -0500678 // This check gives us an early out and prevents generation ID churn on the surface.
679 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
680 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400681 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500682 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000683 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684
Matt Sarett03dd6d52017-01-23 12:15:09 -0500685 // Tell our owning surface to bump its generation ID.
686 const bool completeOverwrite =
687 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700688 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700689
Matt Sarett03dd6d52017-01-23 12:15:09 -0500690 // This can still fail, most notably in the case of a invalid color type or alpha type
691 // conversion. We could pull those checks into this function and avoid the unnecessary
692 // generation ID bump. But then we would be performing those checks twice, since they
693 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400694 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000695}
reed@google.com51df9e32010-12-23 19:29:18 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697//////////////////////////////////////////////////////////////////////////////
698
reed2ff1fce2014-12-11 07:07:37 -0800699void SkCanvas::checkForDeferredSave() {
700 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800701 this->doSave();
702 }
703}
704
reedf0090cb2014-11-26 08:55:51 -0800705int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800706#ifdef SK_DEBUG
707 int count = 0;
708 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
709 for (;;) {
710 const MCRec* rec = (const MCRec*)iter.next();
711 if (!rec) {
712 break;
713 }
714 count += 1 + rec->fDeferredSaveCount;
715 }
716 SkASSERT(count == fSaveCount);
717#endif
718 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800719}
720
721int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800722 fSaveCount += 1;
723 fMCRec->fDeferredSaveCount += 1;
724 return this->getSaveCount() - 1; // return our prev value
725}
726
727void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800728 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700729
730 SkASSERT(fMCRec->fDeferredSaveCount > 0);
731 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800732 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800733}
734
Mike Reed00a97642020-01-25 18:42:23 -0500735int SkCanvas::experimental_saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500736 // TODO: add a virtual for this, and update clients (e.g. chrome)
737 int n = this->save();
Mike Reed46f5c5f2020-02-20 15:42:29 -0500738 this->concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500739 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500740 return n;
741}
742
Mike Reed00a97642020-01-25 18:42:23 -0500743int SkCanvas::experimental_saveCamera(const SkScalar projection[16],
744 const SkScalar camera[16]) {
745 SkM44 proj, cam;
746 proj.setColMajor(projection);
747 cam.setColMajor(camera);
748 return this->experimental_saveCamera(proj, cam);
749}
750
reedf0090cb2014-11-26 08:55:51 -0800751void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800752 if (fMCRec->fDeferredSaveCount > 0) {
753 SkASSERT(fSaveCount > 1);
754 fSaveCount -= 1;
755 fMCRec->fDeferredSaveCount -= 1;
756 } else {
757 // check for underflow
758 if (fMCStack.count() > 1) {
759 this->willRestore();
760 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700761 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800762 this->internalRestore();
763 this->didRestore();
764 }
reedf0090cb2014-11-26 08:55:51 -0800765 }
766}
767
768void SkCanvas::restoreToCount(int count) {
769 // sanity check
770 if (count < 1) {
771 count = 1;
772 }
mtkleinf0f14112014-12-12 08:46:25 -0800773
reedf0090cb2014-11-26 08:55:51 -0800774 int n = this->getSaveCount() - count;
775 for (int i = 0; i < n; ++i) {
776 this->restore();
777 }
778}
779
reed2ff1fce2014-12-11 07:07:37 -0800780void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700782 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000784
Mike Reedc42a1cd2017-02-14 14:25:14 -0500785 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786}
787
reed4960eee2015-12-18 07:09:18 -0800788bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400789 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790}
791
reed4960eee2015-12-18 07:09:18 -0800792bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700793 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400794 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
795 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
796 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
797 // filter does not require a decomposed CTM matrix, the filter space and device space are the
798 // same. When it has been decomposed, we want the original image filter node to process the
799 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
800 // longer the original filter, but has the remainder matrix baked into it, and passing in the
801 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
802 // to the original filter node (barring inflation from consecutive calls to mapRect). While
803 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
804 // passing getDeviceClipBounds() to 'imageFilter' is correct.
805 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
806 // be important to more accurately calculate the clip bounds in the layer space for the original
807 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500808 SkIRect clipBounds = this->getDeviceClipBounds();
809 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000811 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000812
reed96e657d2015-03-10 17:30:07 -0700813 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
814
Robert Phillips12078432018-05-17 11:17:39 -0400815 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
816 // If the image filter DAG affects transparent black then we will need to render
817 // out to the clip bounds
818 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000819 }
Robert Phillips12078432018-05-17 11:17:39 -0400820
821 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700822 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700824 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400825 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400827 inputSaveLayerBounds = clipBounds;
828 }
829
830 if (imageFilter) {
831 // expand the clip bounds by the image filter DAG to include extra content that might
832 // be required by the image filters.
833 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
834 SkImageFilter::kReverse_MapDirection,
835 &inputSaveLayerBounds);
836 }
837
838 SkIRect clippedSaveLayerBounds;
839 if (bounds) {
840 // For better or for worse, user bounds currently act as a hard clip on the layer's
841 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
842 clippedSaveLayerBounds = inputSaveLayerBounds;
843 } else {
844 // If there are no user bounds, we don't want to artificially restrict the resulting
845 // layer bounds, so allow the expanded clip bounds free reign.
846 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800848
849 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400850 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800851 if (BoundsAffectsClip(saveLayerFlags)) {
852 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
853 fMCRec->fRasterClip.setEmpty();
854 fDeviceClipBounds.setEmpty();
855 }
856 return false;
857 }
Robert Phillips12078432018-05-17 11:17:39 -0400858 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859
reed4960eee2015-12-18 07:09:18 -0800860 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700861 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400862 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
863 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000864 }
865
866 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400867 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000868 }
Robert Phillips12078432018-05-17 11:17:39 -0400869
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870 return true;
871}
872
reed4960eee2015-12-18 07:09:18 -0800873int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
874 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000875}
876
Cary Clarke041e312018-03-06 13:00:52 -0500877int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700878 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400879 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
880 // no need for the layer (or any of the draws until the matching restore()
881 this->save();
882 this->clipRect({0,0,0,0});
883 } else {
884 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
885 fSaveCount += 1;
886 this->internalSaveLayer(rec, strategy);
887 }
reed4960eee2015-12-18 07:09:18 -0800888 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800889}
890
Mike Reed148b7fd2018-12-18 17:38:18 -0500891int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
892 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
893 // Assuming clips never expand, if the request bounds is outside of the current clip
894 // there is no need to copy/restore the area, so just devolve back to a regular save.
895 this->save();
896 } else {
897 bool doTheWork = this->onDoSaveBehind(bounds);
898 fSaveCount += 1;
899 this->internalSave();
900 if (doTheWork) {
901 this->internalSaveBehind(bounds);
902 }
903 }
904 return this->getSaveCount() - 1;
905}
906
reeda2217ef2016-07-20 06:04:34 -0700907void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500908 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500909 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400910 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
911 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400912 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500913 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
914 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400915 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400916 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
917 // This means that we only have to copy a dst-sized block of pixels out of src and translate
918 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400919 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
920 dstOrigin.y() - src->getOrigin().y(),
921 dst->width(), dst->height());
922 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400923 return;
924 }
925
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400926 auto special = src->snapSpecial(snapBounds);
927 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400928 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
929 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400930 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
931 }
932 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400933 }
934
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400935 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
936 // by the backdrop filter.
937 SkMatrix toRoot, layerMatrix;
938 SkSize scale;
939 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
940 toRoot = SkMatrix::I();
941 layerMatrix = ctm;
942 } else if (ctm.decomposeScale(&scale, &toRoot)) {
943 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
944 } else {
945 // Perspective, for now, do no scaling of the layer itself.
946 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
947 // the matrix, e.g. based on the midpoint of the near/far planes?
948 toRoot = ctm;
949 layerMatrix = SkMatrix::I();
950 }
951
952 // We have to map the dst bounds from the root space into the layer space where filtering will
953 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
954 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
955 // is a safe, conservative estimate.
956 SkMatrix fromRoot;
957 if (!toRoot.invert(&fromRoot)) {
958 return;
959 }
960
961 // This represents what the backdrop filter needs to produce in the layer space, and is sized
962 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
963 SkIRect layerTargetBounds = fromRoot.mapRect(
964 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
965 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
966 // require some extra input pixels.
967 SkIRect layerInputBounds = filter->filterBounds(
968 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
969 &layerTargetBounds);
970
971 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400972 // be the conservative contents required to fill a layerInputBounds-sized surface with the
973 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400974 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
975 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
976 if (!backdropBounds.intersect(srcDevRect)) {
977 return;
978 }
979
980 auto special = src->snapSpecial(backdropBounds);
981 if (!special) {
982 return;
983 }
984
985 SkColorType colorType = src->imageInfo().colorType();
986 if (colorType == kUnknown_SkColorType) {
987 colorType = kRGBA_8888_SkColorType;
988 }
989 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400990
991 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400992 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400993 // Drawing the temporary and final filtered image requires a higher filter quality if the
994 // 'toRoot' transformation is not identity, in order to minimize the impact on already
995 // rendered edges/content.
996 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
997 p.setFilterQuality(kHigh_SkFilterQuality);
998
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400999 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1000 // and stored in a temporary surface, which is then used as the input to the actual filter.
1001 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1002 if (!tmpSurface) {
1003 return;
1004 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001005
1006 auto tmpCanvas = tmpSurface->getCanvas();
1007 tmpCanvas->clear(SK_ColorTRANSPARENT);
1008 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1009 // space, then maps from root space into the layer space, then maps it so the input layer's
1010 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1011 // performed on backdropBounds.
1012 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1013 tmpCanvas->concat(fromRoot);
1014 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001015
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001016 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1017 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1018 special = tmpSurface->makeImageSnapshot();
1019 } else {
1020 // Since there is no extra transform that was done, update the input bounds to reflect
1021 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1022 // was equal to backdropBounds before it was made relative to the src device and cropped.
1023 // When we use the original snapped image directly, just map the update backdrop bounds
1024 // back into the shared layer space
1025 layerInputBounds = backdropBounds;
1026 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001027
1028 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1029 // draw will be 1-1 so there is no need to increase filter quality.
1030 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001031 }
1032
1033 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1034 // layer space. This has to further offset everything so that filter evaluation thinks the
1035 // source image's top left corner is (0, 0).
1036 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1037 // this can be simplified.
1038 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1039 SkMatrix filterCTM = layerMatrix;
1040 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1041 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1042
1043 SkIPoint offset;
1044 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001045 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001046 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1047 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1048 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1049 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050 offset += layerInputBounds.topLeft();
1051
1052 // Manually setting the device's CTM requires accounting for the device's origin.
1053 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001054 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001055 // a global CTM instead of a device CTM.
1056 SkMatrix dstCTM = toRoot;
1057 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001058 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001059
1060 // And because devices don't have a special-image draw function that supports arbitrary
1061 // matrices, we are abusing the asImage() functionality here...
1062 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001063 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001064 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001065 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001066 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1067 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001068 }
robertphillips7354a4b2015-12-16 05:08:27 -08001069}
reed70ee31b2015-12-10 13:44:45 -08001070
Mike Kleine083f7c2018-02-07 12:54:27 -05001071static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001072 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001073 if (prev.bytesPerPixel() <= 4 &&
1074 prev.colorType() != kRGBA_8888_SkColorType &&
1075 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001076 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1077 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1078 ct = kN32_SkColorType;
1079 }
1080 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001081}
1082
reed4960eee2015-12-18 07:09:18 -08001083void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001084 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001085 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001086 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1087
Michael Ludwigeced98b2020-03-03 10:39:41 -05001088 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1089 // saveLayer ignores mask filters, so force it to null
1090 if (paint.get() && paint->getMaskFilter()) {
1091 paint.writable()->setMaskFilter(nullptr);
1092 }
1093
Mike Reed5532c2a2019-02-23 12:00:32 -05001094 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1095 // regardless of any hint-rect from the caller. skbug.com/8783
1096 if (rec.fBackdrop) {
1097 bounds = nullptr;
1098 }
1099
Michael Ludwigeced98b2020-03-03 10:39:41 -05001100 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001101 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001102 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001103
reed8c30a812016-04-20 16:36:51 -07001104 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001105 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1106 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1107 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001108 *
1109 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001110 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1111 * if necessary.
1112 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1113 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001114 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
Michael Ludwig08b260c2019-05-17 11:21:53 -04001115 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001116 * of the original imagefilter, and draw that (via drawSprite)
1117 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1118 *
1119 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1120 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1121 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001122 if (imageFilter) {
1123 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001124 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1125 &modifiedCTM);
1126 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001127 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001128 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001129 modifiedRec = fMCRec;
1130 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001131 imageFilter = modifiedFilter.get();
1132 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001133 }
1134 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1135 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001136 }
reed8c30a812016-04-20 16:36:51 -07001137
junov@chromium.orga907ac32012-02-24 21:54:07 +00001138 // do this before we create the layer. We don't call the public save() since
1139 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001140 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001141
junov@chromium.orga907ac32012-02-24 21:54:07 +00001142 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001143 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001144 if (modifiedRec) {
1145 // In this case there will be no layer in which to stash the matrix so we need to
1146 // revert the prior MCRec to its earlier state.
1147 modifiedRec->fMatrix = stashedMatrix;
1148 }
reed2ff1fce2014-12-11 07:07:37 -08001149 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 }
1151
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001152 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1153 // the clipRectBounds() call above?
1154 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001155 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001156 }
1157
reed8dc0ccb2015-03-20 06:32:52 -07001158 SkPixelGeometry geo = fProps.pixelGeometry();
1159 if (paint) {
reed76033be2015-03-14 10:54:31 -07001160 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001161 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001162 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001163 }
1164 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165
robertphillips5139e502016-07-19 05:10:40 -07001166 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001167 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001168 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001169 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001170 }
reedb2db8982014-11-13 12:41:02 -08001171
Mike Kleine083f7c2018-02-07 12:54:27 -05001172 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001173 if (rec.fSaveLayerFlags & kF16ColorType) {
1174 info = info.makeColorType(kRGBA_F16_SkColorType);
1175 }
reed129ed1c2016-02-22 06:42:31 -08001176
Hal Canary704cd322016-11-07 14:13:52 -05001177 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001178 {
Florin Malita4571e492019-07-16 10:25:58 -04001179 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001180 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001181 const bool trackCoverage =
1182 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001183 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001184 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001185 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001186 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1187 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001188 return;
reed61f501f2015-04-29 08:34:00 -07001189 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001190 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001191 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192
Mike Reedb43a3e02017-02-11 10:18:58 -05001193 // only have a "next" if this new layer doesn't affect the clip (rare)
1194 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 fMCRec->fLayer = layer;
1196 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001197
Mike Reedc61abee2017-02-28 17:45:27 -05001198 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001199 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001200 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001201 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001202
Mike Reedc42a1cd2017-02-14 14:25:14 -05001203 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1204
1205 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1206 if (layer->fNext) {
1207 // need to punch a hole in the previous device, so we don't draw there, given that
1208 // the new top-layer will allow drawing to happen "below" it.
1209 SkRegion hole(ir);
1210 do {
1211 layer = layer->fNext;
1212 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1213 } while (layer->fNext);
1214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215}
1216
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001217int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001218 if (0xFF == alpha) {
1219 return this->saveLayer(bounds, nullptr);
1220 } else {
1221 SkPaint tmpPaint;
1222 tmpPaint.setAlpha(alpha);
1223 return this->saveLayer(bounds, &tmpPaint);
1224 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001225}
1226
Mike Reed148b7fd2018-12-18 17:38:18 -05001227void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001228 SkBaseDevice* device = this->getTopDevice();
1229 if (nullptr == device) { // Do we still need this check???
1230 return;
1231 }
1232
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001233 // Map the local bounds into the top device's coordinate space (this is not
1234 // necessarily the full global CTM transform).
1235 SkIRect devBounds;
1236 if (localBounds) {
1237 SkRect tmp;
1238 device->localToDevice().mapRect(&tmp, *localBounds);
1239 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1240 devBounds.setEmpty();
1241 }
1242 } else {
1243 devBounds = device->devClipBounds();
1244 }
1245 if (devBounds.isEmpty()) {
1246 return;
1247 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001248
Michael Ludwigac352122019-08-28 21:03:05 +00001249 // This is getting the special image from the current device, which is then drawn into (both by
1250 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1251 // own device, we need to explicitly copy the back image contents so that its original content
1252 // is available when we splat it back later during restore.
1253 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001254 if (!backImage) {
1255 return;
1256 }
1257
1258 // we really need the save, so we can wack the fMCRec
1259 this->checkForDeferredSave();
1260
1261 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1262
1263 SkPaint paint;
1264 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001265 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001266}
1267
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268void SkCanvas::internalRestore() {
1269 SkASSERT(fMCStack.count() != 0);
1270
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001271 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 DeviceCM* layer = fMCRec->fLayer; // may be null
1273 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001274 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275
Mike Reed148b7fd2018-12-18 17:38:18 -05001276 // move this out before we do the actual restore
1277 auto backImage = std::move(fMCRec->fBackImage);
1278
Mike Reedb18e74d2020-01-16 13:58:22 -05001279 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1280 fCameraStack.pop_back();
1281 }
1282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 // now do the normal restore()
1284 fMCRec->~MCRec(); // balanced in save()
1285 fMCStack.pop_back();
1286 fMCRec = (MCRec*)fMCStack.back();
1287
Mike Reedc42a1cd2017-02-14 14:25:14 -05001288 if (fMCRec) {
1289 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1290 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001291
Mike Reed148b7fd2018-12-18 17:38:18 -05001292 if (backImage) {
1293 SkPaint paint;
1294 paint.setBlendMode(SkBlendMode::kDstOver);
1295 const int x = backImage->fLoc.x();
1296 const int y = backImage->fLoc.y();
1297 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1298 nullptr, SkMatrix::I());
1299 }
1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1302 since if we're being recorded, we don't want to record this (the
1303 recorder will have already recorded the restore).
1304 */
bsalomon49f085d2014-09-05 13:34:00 -07001305 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001306 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001307 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001308 // At this point, 'layer' has been removed from the device stack, so the devices that
1309 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1310 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001311 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001312 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001313 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001314 delete layer;
reedb679ca82015-04-07 04:40:48 -07001315 } else {
1316 // we're at the root
reeda499f902015-05-01 09:34:31 -07001317 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001318 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001319 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001321 }
msarettfbfa2582016-08-12 08:29:08 -07001322
1323 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001324 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001325 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
reede8f30622016-03-23 18:59:25 -07001329sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001330 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001331 props = &fProps;
1332 }
1333 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001334}
1335
reede8f30622016-03-23 18:59:25 -07001336sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001337 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001338 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001339}
1340
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001341SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001342 return this->onImageInfo();
1343}
1344
1345SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001346 SkBaseDevice* dev = this->getDevice();
1347 if (dev) {
1348 return dev->imageInfo();
1349 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001350 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001351 }
1352}
1353
brianosman898235c2016-04-06 07:38:23 -07001354bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001355 return this->onGetProps(props);
1356}
1357
1358bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001359 SkBaseDevice* dev = this->getDevice();
1360 if (dev) {
1361 if (props) {
1362 *props = fProps;
1363 }
1364 return true;
1365 } else {
1366 return false;
1367 }
1368}
1369
reed6ceeebd2016-03-09 14:26:26 -08001370bool SkCanvas::peekPixels(SkPixmap* pmap) {
1371 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001372}
1373
reed884e97c2015-05-26 11:31:54 -07001374bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001376 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377}
1378
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001379void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001380 SkPixmap pmap;
1381 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001382 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001383 }
1384 if (info) {
1385 *info = pmap.info();
1386 }
1387 if (rowBytes) {
1388 *rowBytes = pmap.rowBytes();
1389 }
1390 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001391 // If the caller requested the origin, they presumably are expecting the returned pixels to
1392 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1393 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1394 // instead of an origin, just don't expose the pixels in that case. Note that this means
1395 // that layers with complex coordinate spaces can still report their pixels if the caller
1396 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1397 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1398 *origin = this->getTopDevice()->getOrigin();
1399 } else {
1400 return nullptr;
1401 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001402 }
reed884e97c2015-05-26 11:31:54 -07001403 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001404}
1405
reed884e97c2015-05-26 11:31:54 -07001406bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001407 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001408 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001409}
1410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
Mike Reed8bcd1282019-03-13 16:51:54 -04001413// In our current design/features, we should never have a layer (src) in a different colorspace
1414// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1415// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1416// colorspace.
1417static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1418 SkASSERT(src == dst);
1419}
1420
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001421void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001422 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001424 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 paint = &tmp;
1426 }
reed@google.com4b226022011-01-11 18:32:13 +00001427
Mike Reed38992392019-07-30 10:48:15 -04001428 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001431 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001432 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1433 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001434 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001435 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001436 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1437 // so it should always be possible to use the relative origin. Once drawDevice() and
1438 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1439 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001440 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001441 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1442 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001443 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1444 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001445 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1446 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001447 }
reed@google.com76dd2772012-01-05 21:15:07 +00001448 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001449 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 }
reeda2217ef2016-07-20 06:04:34 -07001452
Mike Reed38992392019-07-30 10:48:15 -04001453 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454}
1455
reed32704672015-12-16 08:27:10 -08001456/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001457
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001458void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001459 if (dx || dy) {
1460 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001461 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001462
reedfe69b502016-09-12 06:31:48 -07001463 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001464 // However, if either is non-finite, we might still complicate the matrix type,
1465 // so we still have to compute this.
1466 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001467
Mike Reedc42a1cd2017-02-14 14:25:14 -05001468 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001469
reedfe69b502016-09-12 06:31:48 -07001470 this->didTranslate(dx,dy);
1471 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001475 if (sx != 1 || sy != 1) {
1476 this->checkForDeferredSave();
1477 fMCRec->fMatrix.preScale(sx, sy);
1478
1479 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1480 // but pre-scaling by a non-finite does change it, so we have to recompute.
1481 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1482
1483 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1484
1485 this->didScale(sx, sy);
1486 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487}
1488
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001489void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001490 SkMatrix m;
1491 m.setRotate(degrees);
1492 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
bungeman7438bfc2016-07-12 15:01:19 -07001495void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1496 SkMatrix m;
1497 m.setRotate(degrees, px, py);
1498 this->concat(m);
1499}
1500
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001501void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001502 SkMatrix m;
1503 m.setSkew(sx, sy);
1504 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505}
1506
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001507void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001508 if (matrix.isIdentity()) {
1509 return;
1510 }
1511
reed2ff1fce2014-12-11 07:07:37 -08001512 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001513 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001514
msarett9637ea92016-08-18 14:03:30 -07001515 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001516
Mike Reed7627fa52017-02-08 10:07:53 -05001517 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001518
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001519 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001520}
1521
Mike Reed46f5c5f2020-02-20 15:42:29 -05001522void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001523 this->checkForDeferredSave();
1524
Mike Reed46f5c5f2020-02-20 15:42:29 -05001525 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001526
1527 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1528
1529 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1530
Mike Reed46f5c5f2020-02-20 15:42:29 -05001531 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001532}
Mike Reed064c7f92020-01-08 17:33:04 -05001533
Mike Reed46f5c5f2020-02-20 15:42:29 -05001534void SkCanvas::concat44(const SkM44& m) {
1535 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001536}
1537
reed8c30a812016-04-20 16:36:51 -07001538void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001539 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001540 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001541
Mike Reedc42a1cd2017-02-14 14:25:14 -05001542 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001543}
1544
1545void SkCanvas::setMatrix(const SkMatrix& matrix) {
1546 this->checkForDeferredSave();
1547 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001548 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549}
1550
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001552 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553}
1554
1555//////////////////////////////////////////////////////////////////////////////
1556
Mike Reedc1f77742016-12-09 09:00:50 -05001557void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001558 if (!rect.isFinite()) {
1559 return;
1560 }
reed2ff1fce2014-12-11 07:07:37 -08001561 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1563 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001564}
1565
Mike Reedc1f77742016-12-09 09:00:50 -05001566void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001567 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001568
Mike Reed7627fa52017-02-08 10:07:53 -05001569 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001570
reedc64eff52015-11-21 12:39:45 -08001571 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001572 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1573 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001574 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001577void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1578 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001579 if (fClipRestrictionRect.isEmpty()) {
1580 // we notify the device, but we *dont* resolve deferred saves (since we're just
1581 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001582 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001583 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001584 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001585 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001586 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001587 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001588 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1589 }
1590}
1591
Mike Reedc1f77742016-12-09 09:00:50 -05001592void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001593 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001595 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1597 } else {
1598 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001599 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001601
Mike Reedc1f77742016-12-09 09:00:50 -05001602void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001604
Brian Salomona3b45d42016-10-03 11:36:16 -04001605 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001606
Mike Reed7627fa52017-02-08 10:07:53 -05001607 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001608
Mike Reed20800c82017-11-15 16:09:04 -05001609 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1610 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001611 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001612}
1613
Mike Reedc1f77742016-12-09 09:00:50 -05001614void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001615 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001617
1618 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1619 SkRect r;
1620 if (path.isRect(&r)) {
1621 this->onClipRect(r, op, edgeStyle);
1622 return;
1623 }
1624 SkRRect rrect;
1625 if (path.isOval(&r)) {
1626 rrect.setOval(r);
1627 this->onClipRRect(rrect, op, edgeStyle);
1628 return;
1629 }
1630 if (path.isRRect(&rrect)) {
1631 this->onClipRRect(rrect, op, edgeStyle);
1632 return;
1633 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 }
robertphillips39f05382015-11-24 09:30:12 -08001635
1636 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637}
1638
Mike Reedc1f77742016-12-09 09:00:50 -05001639void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001640 AutoValidateClip avc(this);
1641
Brian Salomona3b45d42016-10-03 11:36:16 -04001642 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001643
Mike Reed7627fa52017-02-08 10:07:53 -05001644 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645
Brian Salomona3b45d42016-10-03 11:36:16 -04001646 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001647 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001648 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001649 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650}
1651
Mike Reedc1f77742016-12-09 09:00:50 -05001652void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001653 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001654 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001655}
1656
Mike Reedc1f77742016-12-09 09:00:50 -05001657void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001658 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001659
reed@google.com5c3d1472011-02-22 19:12:23 +00001660 AutoValidateClip avc(this);
1661
Mike Reed20800c82017-11-15 16:09:04 -05001662 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001663 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664}
1665
reed@google.com819c9212011-02-23 18:56:55 +00001666#ifdef SK_DEBUG
1667void SkCanvas::validateClip() const {
1668 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001669 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001670 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001671 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001672 return;
1673 }
reed@google.com819c9212011-02-23 18:56:55 +00001674}
1675#endif
1676
Mike Reeda1361362017-03-07 09:37:29 -05001677bool SkCanvas::androidFramework_isClipAA() const {
1678 bool containsAA = false;
1679
1680 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1681
1682 return containsAA;
1683}
1684
1685class RgnAccumulator {
1686 SkRegion* fRgn;
1687public:
1688 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1689 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1690 SkIPoint origin = device->getOrigin();
1691 if (origin.x() | origin.y()) {
1692 rgn->translate(origin.x(), origin.y());
1693 }
1694 fRgn->op(*rgn, SkRegion::kUnion_Op);
1695 }
1696};
1697
1698void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1699 RgnAccumulator accum(rgn);
1700 SkRegion tmp;
1701
1702 rgn->setEmpty();
1703 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001704}
1705
reed@google.com5c3d1472011-02-22 19:12:23 +00001706///////////////////////////////////////////////////////////////////////////////
1707
reed@google.com754de5f2014-02-24 19:38:20 +00001708bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001709 return fMCRec->fRasterClip.isEmpty();
1710
1711 // TODO: should we only use the conservative answer in a recording canvas?
1712#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001713 SkBaseDevice* dev = this->getTopDevice();
1714 // if no device we return true
1715 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001716#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001717}
1718
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001719bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001720 SkBaseDevice* dev = this->getTopDevice();
1721 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001722 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001723}
1724
msarettfbfa2582016-08-12 08:29:08 -07001725static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1726#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1727 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1728 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1729 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1730 return 0xF != _mm_movemask_ps(mask);
1731#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1732 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1733 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1734 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1735 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1736#else
1737 SkRect devRectAsRect;
1738 SkRect devClipAsRect;
1739 devRect.store(&devRectAsRect.fLeft);
1740 devClip.store(&devClipAsRect.fLeft);
1741 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1742#endif
1743}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001744
msarettfbfa2582016-08-12 08:29:08 -07001745// It's important for this function to not be inlined. Otherwise the compiler will share code
1746// between the fast path and the slow path, resulting in two slow paths.
1747static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1748 const SkMatrix& matrix) {
1749 SkRect deviceRect;
1750 matrix.mapRect(&deviceRect, src);
1751 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1752}
1753
1754bool SkCanvas::quickReject(const SkRect& src) const {
1755#ifdef SK_DEBUG
1756 // Verify that fDeviceClipBounds are set properly.
1757 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001758 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001759 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001760 } else {
msarettfbfa2582016-08-12 08:29:08 -07001761 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 }
msarettfbfa2582016-08-12 08:29:08 -07001763
msarett9637ea92016-08-18 14:03:30 -07001764 // Verify that fIsScaleTranslate is set properly.
1765 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001766#endif
1767
msarett9637ea92016-08-18 14:03:30 -07001768 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001769 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1770 }
1771
1772 // We inline the implementation of mapScaleTranslate() for the fast path.
1773 float sx = fMCRec->fMatrix.getScaleX();
1774 float sy = fMCRec->fMatrix.getScaleY();
1775 float tx = fMCRec->fMatrix.getTranslateX();
1776 float ty = fMCRec->fMatrix.getTranslateY();
1777 Sk4f scale(sx, sy, sx, sy);
1778 Sk4f trans(tx, ty, tx, ty);
1779
1780 // Apply matrix.
1781 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1782
1783 // Make sure left < right, top < bottom.
1784 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1785 Sk4f min = Sk4f::Min(ltrb, rblt);
1786 Sk4f max = Sk4f::Max(ltrb, rblt);
1787 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1788 // ARM this sequence generates the fastest (a single instruction).
1789 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1790
1791 // Check if the device rect is NaN or outside the clip.
1792 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793}
1794
reed@google.com3b3e8952012-08-16 20:53:31 +00001795bool SkCanvas::quickReject(const SkPath& path) const {
1796 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797}
1798
Mike Klein83c8dd92017-11-28 17:08:45 -05001799SkRect SkCanvas::getLocalClipBounds() const {
1800 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001801 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001802 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 }
1804
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001805 SkMatrix inverse;
1806 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001807 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001808 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001809 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810
Mike Reed42e8c532017-01-23 14:09:13 -05001811 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001812 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001813 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001814
Mike Reedb57b9312018-04-23 12:12:54 -04001815 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001816 inverse.mapRect(&bounds, r);
1817 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818}
1819
Mike Klein83c8dd92017-11-28 17:08:45 -05001820SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001821 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001822}
1823
Mike Reedb18e74d2020-01-16 13:58:22 -05001824///////////////////////////////////////////////////////////////////////
1825
1826SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1827 : fMCRec(owner)
1828 , fCamera(camera)
1829{
1830 // assumes the mcrec has already been concatenated with the camera
1831 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1832 fInvPostCamera.setIdentity();
1833 }
1834}
1835
Mike Reed403c8072020-01-08 10:40:39 -05001836SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001837 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838}
1839
Mike Reed46f5c5f2020-02-20 15:42:29 -05001840SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001841 return fMCRec->fMatrix;
1842}
1843
Mike Reedc43f2a02020-01-16 14:54:34 -05001844SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001845 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001846 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001847 } else {
1848 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001849 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001850 }
1851}
1852
Mike Reedc43f2a02020-01-16 14:54:34 -05001853SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001854 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001855 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001856 } else {
1857 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001858 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001859 }
1860}
1861
Mike Reed46f5c5f2020-02-20 15:42:29 -05001862void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1863 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001864}
1865
1866void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1867 this->experimental_getLocalToWorld().getColMajor(colMajor);
1868}
1869
1870void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1871 this->experimental_getLocalToCamera().getColMajor(colMajor);
1872}
1873
Brian Osman11052242016-10-27 14:47:55 -04001874GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001875 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001876 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001877}
1878
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001879GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001880 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001881 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001882}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001883
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001884void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1885 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001886 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001887 if (outer.isEmpty()) {
1888 return;
1889 }
1890 if (inner.isEmpty()) {
1891 this->drawRRect(outer, paint);
1892 return;
1893 }
1894
1895 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001896 // be able to return ...
1897 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001898 //
1899 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001900 if (!outer.getBounds().contains(inner.getBounds())) {
1901 return;
1902 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001903
1904 this->onDrawDRRect(outer, inner, paint);
1905}
1906
reed41af9662015-01-05 07:49:08 -08001907void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001908 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001909 this->onDrawPaint(paint);
1910}
1911
1912void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001913 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001914 // To avoid redundant logic in our culling code and various backends, we always sort rects
1915 // before passing them along.
1916 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001917}
1918
Mike Reedd5674082019-04-19 15:00:47 -04001919void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1920 TRACE_EVENT0("skia", TRACE_FUNC);
1921 this->onDrawBehind(paint);
1922}
1923
msarettdca352e2016-08-26 06:37:45 -07001924void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001925 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001926 if (region.isEmpty()) {
1927 return;
1928 }
1929
1930 if (region.isRect()) {
1931 return this->drawIRect(region.getBounds(), paint);
1932 }
1933
1934 this->onDrawRegion(region, paint);
1935}
1936
reed41af9662015-01-05 07:49:08 -08001937void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001938 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001939 // To avoid redundant logic in our culling code and various backends, we always sort rects
1940 // before passing them along.
1941 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001942}
1943
1944void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001945 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001946 this->onDrawRRect(rrect, paint);
1947}
1948
1949void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001950 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001951 this->onDrawPoints(mode, count, pts, paint);
1952}
1953
Mike Reede88a1cb2017-03-17 09:50:46 -04001954void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1955 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001956 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001957}
1958
1959void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001960 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001961 RETURN_ON_NULL(vertices);
Mike Reed5caf9352020-03-02 14:57:09 -05001962 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1963 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
1964 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001965}
1966
1967void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001968 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001969 this->onDrawPath(path, paint);
1970}
1971
reeda85d4d02015-05-06 12:56:48 -07001972void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001973 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001974 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001975 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001976}
1977
Mike Reedc4e31092018-01-30 11:15:27 -05001978// Returns true if the rect can be "filled" : non-empty and finite
1979static bool fillable(const SkRect& r) {
1980 SkScalar w = r.width();
1981 SkScalar h = r.height();
1982 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1983}
1984
reede47829b2015-08-06 10:02:53 -07001985void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1986 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001987 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001988 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001989 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001990 return;
1991 }
1992 this->onDrawImageRect(image, &src, dst, paint, constraint);
1993}
reed41af9662015-01-05 07:49:08 -08001994
reed84984ef2015-07-17 07:09:43 -07001995void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1996 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001997 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001998 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001999}
2000
Brian Salomonf08002c2018-10-26 16:15:46 -04002001void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002002 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002003 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002004 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002005}
reede47829b2015-08-06 10:02:53 -07002006
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002007namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002008class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002009public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002010 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2011 if (!origPaint) {
2012 return;
2013 }
2014 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2015 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2016 }
2017 if (origPaint->getMaskFilter()) {
2018 fPaint.writable()->setMaskFilter(nullptr);
2019 }
2020 if (origPaint->isAntiAlias()) {
2021 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002022 }
2023 }
2024
2025 const SkPaint* get() const {
2026 return fPaint;
2027 }
2028
2029private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002030 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002031};
2032} // namespace
2033
reed4c21dc52015-06-25 12:32:03 -07002034void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2035 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002036 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002037 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002038 if (dst.isEmpty()) {
2039 return;
2040 }
msarett552bca92016-08-03 06:53:26 -07002041 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002042 LatticePaint latticePaint(paint);
2043 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002044 } else {
reede47829b2015-08-06 10:02:53 -07002045 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002046 }
reed4c21dc52015-06-25 12:32:03 -07002047}
2048
msarett16882062016-08-16 09:31:08 -07002049void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2050 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002051 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002052 RETURN_ON_NULL(image);
2053 if (dst.isEmpty()) {
2054 return;
2055 }
msarett71df2d72016-09-30 12:41:42 -07002056
2057 SkIRect bounds;
2058 Lattice latticePlusBounds = lattice;
2059 if (!latticePlusBounds.fBounds) {
2060 bounds = SkIRect::MakeWH(image->width(), image->height());
2061 latticePlusBounds.fBounds = &bounds;
2062 }
2063
2064 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002065 LatticePaint latticePaint(paint);
2066 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002067 } else {
2068 this->drawImageRect(image, dst, paint);
2069 }
2070}
2071
reed41af9662015-01-05 07:49:08 -08002072void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002073 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002074 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002075 return;
2076 }
reed41af9662015-01-05 07:49:08 -08002077 this->onDrawBitmap(bitmap, dx, dy, paint);
2078}
2079
reede47829b2015-08-06 10:02:53 -07002080void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002081 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002082 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002083 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002084 return;
2085 }
reede47829b2015-08-06 10:02:53 -07002086 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002087}
2088
reed84984ef2015-07-17 07:09:43 -07002089void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2090 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002091 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002092}
2093
reede47829b2015-08-06 10:02:53 -07002094void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2095 SrcRectConstraint constraint) {
2096 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2097 constraint);
2098}
reede47829b2015-08-06 10:02:53 -07002099
reed41af9662015-01-05 07:49:08 -08002100void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2101 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002102 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002103 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002104 return;
2105 }
msarett552bca92016-08-03 06:53:26 -07002106 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002107 LatticePaint latticePaint(paint);
2108 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002109 } else {
reeda5517e22015-07-14 10:54:12 -07002110 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002111 }
reed41af9662015-01-05 07:49:08 -08002112}
2113
msarettc573a402016-08-02 08:05:56 -07002114void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2115 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002116 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002117 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002118 return;
2119 }
msarett71df2d72016-09-30 12:41:42 -07002120
2121 SkIRect bounds;
2122 Lattice latticePlusBounds = lattice;
2123 if (!latticePlusBounds.fBounds) {
2124 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2125 latticePlusBounds.fBounds = &bounds;
2126 }
2127
2128 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002129 LatticePaint latticePaint(paint);
2130 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002131 } else {
msarett16882062016-08-16 09:31:08 -07002132 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002133 }
msarettc573a402016-08-02 08:05:56 -07002134}
2135
reed71c3c762015-06-24 10:29:17 -07002136void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002137 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002138 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002139 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002140 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002141 if (count <= 0) {
2142 return;
2143 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002144 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002145 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002146 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002147}
2148
reedf70b5312016-03-04 16:36:20 -08002149void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002150 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002151 if (key) {
2152 this->onDrawAnnotation(rect, key, value);
2153 }
2154}
2155
reede47829b2015-08-06 10:02:53 -07002156void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2157 const SkPaint* paint, SrcRectConstraint constraint) {
2158 if (src) {
2159 this->drawImageRect(image, *src, dst, paint, constraint);
2160 } else {
2161 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2162 dst, paint, constraint);
2163 }
2164}
2165void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2166 const SkPaint* paint, SrcRectConstraint constraint) {
2167 if (src) {
2168 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2169 } else {
2170 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2171 dst, paint, constraint);
2172 }
2173}
2174
Mike Reed4204da22017-05-17 08:53:36 -04002175void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002176 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002177 this->onDrawShadowRec(path, rec);
2178}
2179
2180void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2181 SkPaint paint;
2182 const SkRect& pathBounds = path.getBounds();
2183
Mike Reed38992392019-07-30 10:48:15 -04002184 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002185 while (iter.next()) {
2186 iter.fDevice->drawShadow(path, rec);
2187 }
Mike Reed38992392019-07-30 10:48:15 -04002188 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002189}
2190
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002191void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002192 QuadAAFlags aaFlags, const SkColor4f& color,
2193 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002194 TRACE_EVENT0("skia", TRACE_FUNC);
2195 // Make sure the rect is sorted before passing it along
2196 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2197}
2198
2199void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2200 const SkPoint dstClips[],
2201 const SkMatrix preViewMatrices[],
2202 const SkPaint* paint,
2203 SrcRectConstraint constraint) {
2204 TRACE_EVENT0("skia", TRACE_FUNC);
2205 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2206}
2207
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208//////////////////////////////////////////////////////////////////////////////
2209// These are the virtual drawing methods
2210//////////////////////////////////////////////////////////////////////////////
2211
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002212void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002213 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002214 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2215 }
2216}
2217
reed41af9662015-01-05 07:49:08 -08002218void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002219 this->internalDrawPaint(paint);
2220}
2221
2222void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002223 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224
2225 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002226 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227 }
2228
Mike Reed38992392019-07-30 10:48:15 -04002229 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230}
2231
reed41af9662015-01-05 07:49:08 -08002232void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2233 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002234 if ((long)count <= 0) {
2235 return;
2236 }
2237
Mike Reed822128b2017-02-28 16:41:03 -05002238 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002239 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002240 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002241 // special-case 2 points (common for drawing a single line)
2242 if (2 == count) {
2243 r.set(pts[0], pts[1]);
2244 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002245 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002246 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002247 if (!r.isFinite()) {
2248 return;
2249 }
Mike Reed822128b2017-02-28 16:41:03 -05002250 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002251 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2252 return;
2253 }
2254 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002255 }
reed@google.coma584aed2012-05-16 14:06:02 +00002256
halcanary96fcdcc2015-08-27 07:41:13 -07002257 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258
Mike Reed38992392019-07-30 10:48:15 -04002259 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002260
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002262 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263 }
reed@google.com4b226022011-01-11 18:32:13 +00002264
Mike Reed38992392019-07-30 10:48:15 -04002265 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266}
2267
reed4a167172016-08-18 17:15:25 -07002268static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002269 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002270}
2271
reed41af9662015-01-05 07:49:08 -08002272void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002273 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002275 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002276 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002277 return;
2278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 }
reed@google.com4b226022011-01-11 18:32:13 +00002280
reed4a167172016-08-18 17:15:25 -07002281 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002282 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283
reed4a167172016-08-18 17:15:25 -07002284 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002285 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002286 }
2287
Mike Reed38992392019-07-30 10:48:15 -04002288 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002289 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002290 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002291 SkDrawIter iter(this);
2292 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002293 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002294 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002296}
2297
msarett44df6512016-08-25 13:54:30 -07002298void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002299 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002300 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002301 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002302 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2303 return;
2304 }
msarett44df6512016-08-25 13:54:30 -07002305 }
2306
Mike Reed38992392019-07-30 10:48:15 -04002307 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002308
2309 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002310 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002311 }
2312
Mike Reed38992392019-07-30 10:48:15 -04002313 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002314}
2315
Mike Reedd5674082019-04-19 15:00:47 -04002316void SkCanvas::onDrawBehind(const SkPaint& paint) {
2317 SkIRect bounds;
2318 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2319 for (;;) {
2320 const MCRec* rec = (const MCRec*)iter.prev();
2321 if (!rec) {
2322 return; // no backimages, so nothing to draw
2323 }
2324 if (rec->fBackImage) {
2325 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2326 rec->fBackImage->fImage->width(),
2327 rec->fBackImage->fImage->height());
2328 break;
2329 }
2330 }
2331
Mike Reed38992392019-07-30 10:48:15 -04002332 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002333
2334 while (iter.next()) {
2335 SkBaseDevice* dev = iter.fDevice;
2336
Mike Reedd5674082019-04-19 15:00:47 -04002337 dev->save();
2338 // We use clipRegion because it is already defined to operate in dev-space
2339 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2340 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002341 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002342 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002343 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002344 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002345 }
2346
Mike Reed38992392019-07-30 10:48:15 -04002347 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002348}
2349
reed41af9662015-01-05 07:49:08 -08002350void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002351 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002352 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002353 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002354 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002355 return;
2356 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002357 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002358
Mike Reed38992392019-07-30 10:48:15 -04002359 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002360
2361 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002362 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002363 }
2364
Mike Reed38992392019-07-30 10:48:15 -04002365 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002366}
2367
bsalomonac3aa242016-08-19 11:25:19 -07002368void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2369 SkScalar sweepAngle, bool useCenter,
2370 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002371 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002372 if (paint.canComputeFastBounds()) {
2373 SkRect storage;
2374 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002375 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002376 return;
2377 }
bsalomonac3aa242016-08-19 11:25:19 -07002378 }
2379
Mike Reed38992392019-07-30 10:48:15 -04002380 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002381
2382 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002383 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002384 }
2385
Mike Reed38992392019-07-30 10:48:15 -04002386 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002387}
2388
reed41af9662015-01-05 07:49:08 -08002389void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002390 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002391 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002392 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2393 return;
2394 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002395 }
2396
2397 if (rrect.isRect()) {
2398 // call the non-virtual version
2399 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002400 return;
2401 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002402 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002403 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2404 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002405 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002406
Mike Reed38992392019-07-30 10:48:15 -04002407 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002408
2409 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002410 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002411 }
2412
Mike Reed38992392019-07-30 10:48:15 -04002413 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002414}
2415
Mike Reed822128b2017-02-28 16:41:03 -05002416void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002417 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002418 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002419 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2420 return;
2421 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002422 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002423
Mike Reed38992392019-07-30 10:48:15 -04002424 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002425
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002426 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002427 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002428 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002429
Mike Reed38992392019-07-30 10:48:15 -04002430 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002431}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002432
reed41af9662015-01-05 07:49:08 -08002433void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002434 if (!path.isFinite()) {
2435 return;
2436 }
2437
Mike Reed822128b2017-02-28 16:41:03 -05002438 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002439 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002440 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002441 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2442 return;
2443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002445
Mike Reed822128b2017-02-28 16:41:03 -05002446 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002447 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002448 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002449 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002450 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002451 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002452
Mike Reed38992392019-07-30 10:48:15 -04002453 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002454
2455 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002456 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457 }
2458
Mike Reed38992392019-07-30 10:48:15 -04002459 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460}
2461
reed262a71b2015-12-05 13:07:27 -08002462bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002463 if (!paint.getImageFilter()) {
2464 return false;
2465 }
2466
2467 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002468 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002469 return false;
2470 }
2471
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002472 // The other paint effects need to be applied before the image filter, but the sprite draw
2473 // applies the filter explicitly first.
2474 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2475 return false;
2476 }
reed262a71b2015-12-05 13:07:27 -08002477 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2478 // Once we can filter and the filter will return a result larger than itself, we should be
2479 // able to remove this constraint.
2480 // skbug.com/4526
2481 //
2482 SkPoint pt;
2483 ctm.mapXY(x, y, &pt);
2484 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2485 return ir.contains(fMCRec->fRasterClip.getBounds());
2486}
2487
Mike Reedf441cfc2018-04-11 14:50:16 -04002488// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2489// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2490// null.
2491static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2492 if (paintParam) {
2493 *real = *paintParam;
2494 real->setStyle(SkPaint::kFill_Style);
2495 real->setPathEffect(nullptr);
2496 paintParam = real;
2497 }
2498 return paintParam;
2499}
2500
reeda85d4d02015-05-06 12:56:48 -07002501void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002502 SkPaint realPaint;
2503 paint = init_image_paint(&realPaint, paint);
2504
reeda85d4d02015-05-06 12:56:48 -07002505 SkRect bounds = SkRect::MakeXYWH(x, y,
2506 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002507 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002508 SkRect tmp = bounds;
2509 if (paint) {
2510 paint->computeFastBounds(tmp, &tmp);
2511 }
2512 if (this->quickReject(tmp)) {
2513 return;
2514 }
reeda85d4d02015-05-06 12:56:48 -07002515 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002516 // At this point we need a real paint object. If the caller passed null, then we should
2517 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2518 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2519 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002520
reeda2217ef2016-07-20 06:04:34 -07002521 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002522 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2523 *paint);
2524 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002525 special = this->getDevice()->makeSpecial(image);
2526 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002527 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002528 }
2529 }
2530
Mike Reed38992392019-07-30 10:48:15 -04002531 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002532
reeda85d4d02015-05-06 12:56:48 -07002533 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002534 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002535 if (special) {
2536 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002537 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002538 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002539 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002540 SkScalarRoundToInt(pt.fY), pnt,
2541 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002542 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002543 iter.fDevice->drawImageRect(
2544 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2545 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002546 }
reeda85d4d02015-05-06 12:56:48 -07002547 }
halcanary9d524f22016-03-29 09:03:52 -07002548
Mike Reed38992392019-07-30 10:48:15 -04002549 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002550}
2551
reed41af9662015-01-05 07:49:08 -08002552void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002553 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002554 SkPaint realPaint;
2555 paint = init_image_paint(&realPaint, paint);
2556
halcanary96fcdcc2015-08-27 07:41:13 -07002557 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002558 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002559 if (paint) {
2560 paint->computeFastBounds(dst, &storage);
2561 }
2562 if (this->quickReject(storage)) {
2563 return;
2564 }
reeda85d4d02015-05-06 12:56:48 -07002565 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002566 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002567
Mike Reed38992392019-07-30 10:48:15 -04002568 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002569
reeda85d4d02015-05-06 12:56:48 -07002570 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002571 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002572 }
halcanary9d524f22016-03-29 09:03:52 -07002573
Mike Reed38992392019-07-30 10:48:15 -04002574 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002575}
2576
reed41af9662015-01-05 07:49:08 -08002577void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002578 SkDEBUGCODE(bitmap.validate();)
2579
reed33366972015-10-08 09:22:02 -07002580 if (bitmap.drawsNothing()) {
2581 return;
2582 }
2583
Mike Reedf441cfc2018-04-11 14:50:16 -04002584 SkPaint realPaint;
2585 init_image_paint(&realPaint, paint);
2586 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002587
Mike Reed822128b2017-02-28 16:41:03 -05002588 SkRect bounds;
2589 bitmap.getBounds(&bounds);
2590 bounds.offset(x, y);
2591 bool canFastBounds = paint->canComputeFastBounds();
2592 if (canFastBounds) {
2593 SkRect storage;
2594 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002595 return;
2596 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002597 }
reed@google.com4b226022011-01-11 18:32:13 +00002598
reeda2217ef2016-07-20 06:04:34 -07002599 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002600 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2601 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002602 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002603 special = this->getDevice()->makeSpecial(bitmap);
2604 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002605 drawAsSprite = false;
2606 }
2607 }
2608
Mike Reed38992392019-07-30 10:48:15 -04002609 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002610
2611 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002612 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002613 if (special) {
reed262a71b2015-12-05 13:07:27 -08002614 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002615 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002616 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002617 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002618 SkScalarRoundToInt(pt.fY), pnt,
2619 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002620 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002621 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2622 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2623 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002624 }
reed33366972015-10-08 09:22:02 -07002625 }
msarettfbfa2582016-08-12 08:29:08 -07002626
Mike Reed38992392019-07-30 10:48:15 -04002627 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002628}
2629
reed@google.com9987ec32011-09-07 11:57:52 +00002630// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002631void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002632 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002633 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002634 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002635 return;
2636 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002637
halcanary96fcdcc2015-08-27 07:41:13 -07002638 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002639 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002640 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2641 return;
2642 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002643 }
reed@google.com3d608122011-11-21 15:16:16 +00002644
reed@google.com33535f32012-09-25 15:37:50 +00002645 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002646 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002647 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002648 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002649
Mike Reed38992392019-07-30 10:48:15 -04002650 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002651
reed@google.com33535f32012-09-25 15:37:50 +00002652 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002653 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002654 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002655
Mike Reed38992392019-07-30 10:48:15 -04002656 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002657}
2658
reed41af9662015-01-05 07:49:08 -08002659void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002660 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002661 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002662 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002663}
2664
reed4c21dc52015-06-25 12:32:03 -07002665void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2666 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002667 SkPaint realPaint;
2668 paint = init_image_paint(&realPaint, paint);
2669
halcanary96fcdcc2015-08-27 07:41:13 -07002670 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002671 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002672 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2673 return;
2674 }
reed@google.com3d608122011-11-21 15:16:16 +00002675 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002676 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002677
Mike Reed38992392019-07-30 10:48:15 -04002678 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002679
reed4c21dc52015-06-25 12:32:03 -07002680 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002681 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002682 }
halcanary9d524f22016-03-29 09:03:52 -07002683
Mike Reed38992392019-07-30 10:48:15 -04002684 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002685}
2686
reed41af9662015-01-05 07:49:08 -08002687void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2688 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002689 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002690 SkPaint realPaint;
2691 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002692
halcanary96fcdcc2015-08-27 07:41:13 -07002693 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002694 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002695 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2696 return;
2697 }
reed4c21dc52015-06-25 12:32:03 -07002698 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002699 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002700
Mike Reed38992392019-07-30 10:48:15 -04002701 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002702
reed4c21dc52015-06-25 12:32:03 -07002703 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002704 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002705 }
halcanary9d524f22016-03-29 09:03:52 -07002706
Mike Reed38992392019-07-30 10:48:15 -04002707 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002708}
2709
msarett16882062016-08-16 09:31:08 -07002710void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2711 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002712 SkPaint realPaint;
2713 paint = init_image_paint(&realPaint, paint);
2714
msarett16882062016-08-16 09:31:08 -07002715 if (nullptr == paint || paint->canComputeFastBounds()) {
2716 SkRect storage;
2717 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2718 return;
2719 }
2720 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002721 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002722
Mike Reed38992392019-07-30 10:48:15 -04002723 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002724
2725 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002726 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002727 }
2728
Mike Reed38992392019-07-30 10:48:15 -04002729 DRAW_END
msarett16882062016-08-16 09:31:08 -07002730}
2731
2732void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2733 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002734 SkPaint realPaint;
2735 paint = init_image_paint(&realPaint, paint);
2736
msarett16882062016-08-16 09:31:08 -07002737 if (nullptr == paint || paint->canComputeFastBounds()) {
2738 SkRect storage;
2739 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2740 return;
2741 }
2742 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002743 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002744
Mike Reed38992392019-07-30 10:48:15 -04002745 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002746
2747 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002748 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002749 }
2750
Mike Reed38992392019-07-30 10:48:15 -04002751 DRAW_END
msarett16882062016-08-16 09:31:08 -07002752}
2753
fmalita00d5c2c2014-08-21 08:53:26 -07002754void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2755 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002756 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002757 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002758 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002759 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002760 SkRect tmp;
2761 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2762 return;
2763 }
2764 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002765 }
2766
fmalita024f9962015-03-03 19:08:17 -08002767 // We cannot filter in the looper as we normally do, because the paint is
2768 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002769 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002770
fmalitaaa1b9122014-08-28 14:32:24 -07002771 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002772 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002773 }
2774
Mike Reed38992392019-07-30 10:48:15 -04002775 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002776}
2777
Mike Reed358fcad2018-11-23 15:27:51 -05002778// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002779void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002780 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2781 TRACE_EVENT0("skia", TRACE_FUNC);
2782 if (byteLength) {
2783 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002784 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002785 }
2786}
Mike Reed4f81bb72019-01-23 09:23:00 -05002787
fmalita00d5c2c2014-08-21 08:53:26 -07002788void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2789 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002790 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002791 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002792 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002793 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002794}
reed@google.come0d9ce82014-04-23 04:00:17 +00002795
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002796#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002797void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002798 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002799 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002800
2801 while (iter.next()) {
2802 // In the common case of one iteration we could std::move vertices here.
Mike Reed5caf9352020-03-02 14:57:09 -05002803 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002804 }
2805
Mike Reed38992392019-07-30 10:48:15 -04002806 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002807}
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002808#else
2809void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2810 const SkPaint& paint) {
2811 DRAW_BEGIN(paint, nullptr)
2812
2813 while (iter.next()) {
2814 // In the common case of one iteration we could std::move vertices here.
2815 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2816 }
2817
2818 DRAW_END
2819}
2820#endif
Brian Salomon199fb872017-02-06 09:41:10 -05002821
dandovb3c9d1c2014-08-12 08:34:29 -07002822void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002823 const SkPoint texCoords[4], SkBlendMode bmode,
2824 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002825 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002826 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002827 return;
2828 }
mtklein6cfa73a2014-08-13 13:33:49 -07002829
Mike Reedfaba3712016-11-03 14:45:31 -04002830 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002831}
2832
2833void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002834 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002835 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002836 // Since a patch is always within the convex hull of the control points, we discard it when its
2837 // bounding rectangle is completely outside the current clip.
2838 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002839 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002840 if (this->quickReject(bounds)) {
2841 return;
2842 }
mtklein6cfa73a2014-08-13 13:33:49 -07002843
Mike Reed38992392019-07-30 10:48:15 -04002844 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002845
dandovecfff212014-08-04 10:02:00 -07002846 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002847 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002848 }
mtklein6cfa73a2014-08-13 13:33:49 -07002849
Mike Reed38992392019-07-30 10:48:15 -04002850 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002851}
2852
reeda8db7282015-07-07 10:22:31 -07002853void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002854#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002855 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002856#endif
reede3b38ce2016-01-08 09:18:44 -08002857 RETURN_ON_NULL(dr);
2858 if (x || y) {
2859 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2860 this->onDrawDrawable(dr, &matrix);
2861 } else {
2862 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002863 }
2864}
2865
reeda8db7282015-07-07 10:22:31 -07002866void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002867#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002868 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002869#endif
reede3b38ce2016-01-08 09:18:44 -08002870 RETURN_ON_NULL(dr);
2871 if (matrix && matrix->isIdentity()) {
2872 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002873 }
reede3b38ce2016-01-08 09:18:44 -08002874 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002875}
2876
2877void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002878 // drawable bounds are no longer reliable (e.g. android displaylist)
2879 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002880 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002881}
2882
reed71c3c762015-06-24 10:29:17 -07002883void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002884 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002885 const SkRect* cull, const SkPaint* paint) {
2886 if (cull && this->quickReject(*cull)) {
2887 return;
2888 }
2889
2890 SkPaint pnt;
2891 if (paint) {
2892 pnt = *paint;
2893 }
halcanary9d524f22016-03-29 09:03:52 -07002894
Mike Reed38992392019-07-30 10:48:15 -04002895 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002896 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002897 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002898 }
Mike Reed38992392019-07-30 10:48:15 -04002899 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002900}
2901
reedf70b5312016-03-04 16:36:20 -08002902void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2903 SkASSERT(key);
2904
2905 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002906 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002907 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002908 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002909 }
Mike Reed38992392019-07-30 10:48:15 -04002910 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002911}
2912
Michael Ludwiga595f862019-08-27 15:25:49 -04002913void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2914 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002915 SkASSERT(r.isSorted());
2916
2917 // If this used a paint, it would be a filled color with blend mode, which does not
2918 // need to use an autodraw loop, so use SkDrawIter directly.
2919 if (this->quickReject(r)) {
2920 return;
2921 }
2922
Michael Ludwiga4b44882019-08-28 14:34:58 -04002923 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002924 SkDrawIter iter(this);
2925 while(iter.next()) {
2926 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2927 }
2928}
2929
2930void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2931 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2932 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002933 if (count <= 0) {
2934 // Nothing to draw
2935 return;
2936 }
2937
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002938 SkPaint realPaint;
2939 init_image_paint(&realPaint, paint);
2940
Michael Ludwiga4b44882019-08-28 14:34:58 -04002941 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2942 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2943 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2944 // or we need it for the autolooper (since it greatly improves image filter perf).
2945 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2946 bool setBoundsValid = count == 1 || needsAutoLooper;
2947 SkRect setBounds = imageSet[0].fDstRect;
2948 if (imageSet[0].fMatrixIndex >= 0) {
2949 // Account for the per-entry transform that is applied prior to the CTM when drawing
2950 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002951 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002952 if (needsAutoLooper) {
2953 for (int i = 1; i < count; ++i) {
2954 SkRect entryBounds = imageSet[i].fDstRect;
2955 if (imageSet[i].fMatrixIndex >= 0) {
2956 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2957 }
2958 setBounds.joinPossiblyEmptyRect(entryBounds);
2959 }
2960 }
2961
2962 // If we happen to have the draw bounds, though, might as well check quickReject().
2963 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2964 SkRect tmp;
2965 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2966 return;
2967 }
2968 }
2969
2970 if (needsAutoLooper) {
2971 SkASSERT(setBoundsValid);
2972 DRAW_BEGIN(realPaint, &setBounds)
2973 while (iter.next()) {
2974 iter.fDevice->drawEdgeAAImageSet(
2975 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2976 }
2977 DRAW_END
2978 } else {
2979 this->predrawNotify();
2980 SkDrawIter iter(this);
2981 while(iter.next()) {
2982 iter.fDevice->drawEdgeAAImageSet(
2983 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2984 }
2985 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002986}
2987
reed@android.com8a1c16f2008-12-17 15:59:43 +00002988//////////////////////////////////////////////////////////////////////////////
2989// These methods are NOT virtual, and therefore must call back into virtual
2990// methods, rather than actually drawing themselves.
2991//////////////////////////////////////////////////////////////////////////////
2992
reed374772b2016-10-05 17:33:02 -07002993void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002994 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002995 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002996 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002997 this->drawPaint(paint);
2998}
2999
3000void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05003001 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00003002 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3003}
3004
Mike Reed3661bc92017-02-22 13:21:42 -05003005void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003006 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00003007 pts[0].set(x0, y0);
3008 pts[1].set(x1, y1);
3009 this->drawPoints(kLines_PointMode, 2, pts, paint);
3010}
3011
Mike Reed3661bc92017-02-22 13:21:42 -05003012void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003013 if (radius < 0) {
3014 radius = 0;
3015 }
3016
3017 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04003018 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003019 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003020}
3021
3022void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3023 const SkPaint& paint) {
3024 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003025 SkRRect rrect;
3026 rrect.setRectXY(r, rx, ry);
3027 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003028 } else {
3029 this->drawRect(r, paint);
3030 }
3031}
3032
reed@android.com8a1c16f2008-12-17 15:59:43 +00003033void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3034 SkScalar sweepAngle, bool useCenter,
3035 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04003036 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07003037 if (oval.isEmpty() || !sweepAngle) {
3038 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003039 }
bsalomon21af9ca2016-08-25 12:29:23 -07003040 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003041}
3042
reed@android.comf76bacf2009-05-13 14:00:33 +00003043///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003044#ifdef SK_DISABLE_SKPICTURE
3045void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07003046
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003047
3048void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3049 const SkPaint* paint) {}
3050#else
Mike Klein88d90712018-01-27 17:30:04 +00003051/**
3052 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3053 * against the playback cost of recursing into the subpicture to get at its actual ops.
3054 *
3055 * For now we pick a conservatively small value, though measurement (and other heuristics like
3056 * the type of ops contained) may justify changing this value.
3057 */
3058#define kMaxPictureOpsToUnrollInsteadOfRef 1
3059
reedd5fa1a42014-08-09 11:08:05 -07003060void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04003061 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08003062 RETURN_ON_NULL(picture);
3063
reede3b38ce2016-01-08 09:18:44 -08003064 if (matrix && matrix->isIdentity()) {
3065 matrix = nullptr;
3066 }
Mike Klein88d90712018-01-27 17:30:04 +00003067 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3068 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3069 picture->playback(this);
3070 } else {
3071 this->onDrawPicture(picture, matrix, paint);
3072 }
reedd5fa1a42014-08-09 11:08:05 -07003073}
robertphillips9b14f262014-06-04 05:40:44 -07003074
reedd5fa1a42014-08-09 11:08:05 -07003075void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3076 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003077 if (!paint || paint->canComputeFastBounds()) {
3078 SkRect bounds = picture->cullRect();
3079 if (paint) {
3080 paint->computeFastBounds(bounds, &bounds);
3081 }
3082 if (matrix) {
3083 matrix->mapRect(&bounds);
3084 }
3085 if (this->quickReject(bounds)) {
3086 return;
3087 }
3088 }
3089
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003090 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003091 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003092}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003093#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003094
reed@android.com8a1c16f2008-12-17 15:59:43 +00003095///////////////////////////////////////////////////////////////////////////////
3096///////////////////////////////////////////////////////////////////////////////
3097
reed3aafe112016-08-18 12:45:34 -07003098SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003099 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003100
3101 SkASSERT(canvas);
3102
reed3aafe112016-08-18 12:45:34 -07003103 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003104 fDone = !fImpl->next();
3105}
3106
3107SkCanvas::LayerIter::~LayerIter() {
3108 fImpl->~SkDrawIter();
3109}
3110
3111void SkCanvas::LayerIter::next() {
3112 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003113 if (!fDone) {
3114 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
3115 // filters, so its devices will always be able to report the origin exactly.
3116 fDeviceOrigin = fImpl->fDevice->getOrigin();
3117 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003118}
3119
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003120SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003121 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003122}
3123
3124const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003125 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003126}
3127
3128const SkPaint& SkCanvas::LayerIter::paint() const {
3129 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003130 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003131 paint = &fDefaultPaint;
3132 }
3133 return *paint;
3134}
3135
Mike Reedca37f322018-03-08 13:22:16 -05003136SkIRect SkCanvas::LayerIter::clipBounds() const {
3137 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003138}
3139
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003140int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
3141int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003142
3143///////////////////////////////////////////////////////////////////////////////
3144
Brian Osmane8a98632019-04-10 10:26:10 -04003145SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3146SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3147SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3148SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3149
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003150SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3151 const SkRect& dstRect, int matrixIndex, float alpha,
3152 unsigned aaFlags, bool hasClip)
3153 : fImage(std::move(image))
3154 , fSrcRect(srcRect)
3155 , fDstRect(dstRect)
3156 , fMatrixIndex(matrixIndex)
3157 , fAlpha(alpha)
3158 , fAAFlags(aaFlags)
3159 , fHasClip(hasClip) {}
3160
3161SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3162 const SkRect& dstRect, float alpha, unsigned aaFlags)
3163 : fImage(std::move(image))
3164 , fSrcRect(srcRect)
3165 , fDstRect(dstRect)
3166 , fAlpha(alpha)
3167 , fAAFlags(aaFlags) {}
3168
3169///////////////////////////////////////////////////////////////////////////////
3170
Mike Reed5df49342016-11-12 08:06:55 -06003171std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003172 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003173 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003174 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003175 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003176
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003177 SkBitmap bitmap;
3178 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003179 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003180 }
Mike Reed12f77342017-11-08 11:19:52 -05003181
3182 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003183 std::make_unique<SkCanvas>(bitmap, *props) :
3184 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003185}
reedd5fa1a42014-08-09 11:08:05 -07003186
3187///////////////////////////////////////////////////////////////////////////////
3188
Florin Malitaee424ac2016-12-01 12:47:59 -05003189SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003190 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003191
Florin Malita439ace92016-12-02 12:05:41 -05003192SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003193 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003194
Herb Derbyefe39bc2018-05-01 17:06:20 -04003195SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003196 : INHERITED(device) {}
3197
Florin Malitaee424ac2016-12-01 12:47:59 -05003198SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3199 (void)this->INHERITED::getSaveLayerStrategy(rec);
3200 return kNoLayer_SaveLayerStrategy;
3201}
3202
Mike Reed148b7fd2018-12-18 17:38:18 -05003203bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3204 return false;
3205}
3206
Florin Malitaee424ac2016-12-01 12:47:59 -05003207///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003208
reed73603f32016-09-20 08:42:38 -07003209static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3210static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3211static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3212static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3213static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3214static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003215
3216///////////////////////////////////////////////////////////////////////////////////////////////////
3217
3218SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3219 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003220 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003221 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003222 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003223 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003224 clip.setEmpty();
3225 }
3226
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003227 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003228 return handle;
3229 }
3230 return nullptr;
3231}
3232
3233static bool install(SkBitmap* bm, const SkImageInfo& info,
3234 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003235 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003236}
3237
3238SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3239 SkBitmap* bm) {
3240 SkRasterHandleAllocator::Rec rec;
3241 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3242 return nullptr;
3243 }
3244 return rec.fHandle;
3245}
3246
3247std::unique_ptr<SkCanvas>
3248SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3249 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003250 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003251 return nullptr;
3252 }
3253
3254 SkBitmap bm;
3255 Handle hndl;
3256
3257 if (rec) {
3258 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3259 } else {
3260 hndl = alloc->allocBitmap(info, &bm);
3261 }
3262 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3263}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003264
3265///////////////////////////////////////////////////////////////////////////////////////////////////