blob: 61e7b89f6b31de7107e4bbf8a1f07055effd9d0b [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"
Brian Osmanf11e3312020-03-24 14:57:38 -040020#include "include/effects/SkRuntimeEffect.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/private/SkNx.h"
22#include "include/private/SkTo.h"
23#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040024#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050026#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/core/SkCanvasPriv.h"
28#include "src/core/SkClipOpPriv.h"
29#include "src/core/SkClipStack.h"
30#include "src/core/SkDraw.h"
31#include "src/core/SkGlyphRun.h"
32#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040033#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050034#include "src/core/SkLatticeIter.h"
35#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050036#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "src/core/SkMatrixUtils.h"
38#include "src/core/SkPaintPriv.h"
39#include "src/core/SkRasterClip.h"
40#include "src/core/SkSpecialImage.h"
41#include "src/core/SkStrikeCache.h"
42#include "src/core/SkTLazy.h"
43#include "src/core/SkTextFormatParams.h"
44#include "src/core/SkTraceEvent.h"
Mike Reedba962562020-03-12 20:33:21 -040045#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050046#include "src/image/SkImage_Base.h"
47#include "src/image/SkSurface_Base.h"
48#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040049
bungemand3ebb482015-08-05 13:57:49 -070050#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050053#include "include/gpu/GrContext.h"
54#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000055#endif
56
reede3b38ce2016-01-08 09:18:44 -080057#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050058#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080059
Mike Klein1bb7e232019-12-10 08:58:52 -060060// This is a test: static_assert with no message is a c++17 feature,
61// and std::max() is constexpr only since the c++14 stdlib.
62static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000063
Mike Reed139e5e02017-03-08 11:29:33 -050064///////////////////////////////////////////////////////////////////////////////////////////////////
65
reedc83a2972015-07-16 07:40:45 -070066/*
67 * Return true if the drawing this rect would hit every pixels in the canvas.
68 *
69 * Returns false if
70 * - rect does not contain the canvas' bounds
71 * - paint is not fill
72 * - paint would blur or otherwise change the coverage of the rect
73 */
74bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
75 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070076 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
77 (int)kNone_ShaderOverrideOpacity,
78 "need_matching_enums0");
79 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
80 (int)kOpaque_ShaderOverrideOpacity,
81 "need_matching_enums1");
82 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
83 (int)kNotOpaque_ShaderOverrideOpacity,
84 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070085
86 const SkISize size = this->getBaseLayerSize();
87 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050088
89 // if we're clipped at all, we can't overwrite the entire surface
90 {
91 SkBaseDevice* base = this->getDevice();
92 SkBaseDevice* top = this->getTopDevice();
93 if (base != top) {
94 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
95 }
96 if (!base->clipIsWideOpen()) {
97 return false;
98 }
reedc83a2972015-07-16 07:40:45 -070099 }
100
101 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700102 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700103 return false; // conservative
104 }
halcanaryc5769b22016-08-10 07:13:21 -0700105
106 SkRect devRect;
107 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
108 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700109 return false;
110 }
111 }
112
113 if (paint) {
114 SkPaint::Style paintStyle = paint->getStyle();
115 if (!(paintStyle == SkPaint::kFill_Style ||
116 paintStyle == SkPaint::kStrokeAndFill_Style)) {
117 return false;
118 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400119 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700120 return false; // conservative
121 }
122 }
123 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
124}
125
126///////////////////////////////////////////////////////////////////////////////////////////////////
127
reed@google.comda17f752012-08-16 18:27:05 +0000128// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129//#define SK_TRACE_SAVERESTORE
130
131#ifdef SK_TRACE_SAVERESTORE
132 static int gLayerCounter;
133 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
134 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
135
136 static int gRecCounter;
137 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
138 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
139
140 static int gCanvasCounter;
141 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
142 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
143#else
144 #define inc_layer()
145 #define dec_layer()
146 #define inc_rec()
147 #define dec_rec()
148 #define inc_canvas()
149 #define dec_canvas()
150#endif
151
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000152typedef SkTLazy<SkPaint> SkLazyPaint;
153
reedc83a2972015-07-16 07:40:45 -0700154void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000155 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700156 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
157 ? SkSurface::kDiscard_ContentChangeMode
158 : SkSurface::kRetain_ContentChangeMode);
159 }
160}
161
162void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
163 ShaderOverrideOpacity overrideOpacity) {
164 if (fSurfaceBase) {
165 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
166 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
167 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
168 // and therefore we don't care which mode we're in.
169 //
170 if (fSurfaceBase->outstandingImageSnapshot()) {
171 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
172 mode = SkSurface::kDiscard_ContentChangeMode;
173 }
174 }
175 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000176 }
177}
178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000181/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 The clip/matrix/proc are fields that reflect the top of the save/restore
183 stack. Whenever the canvas changes, it marks a dirty flag, and then before
184 these are used (assuming we're not on a layer) we rebuild these cache
185 values: they reflect the top of the save stack, but translated and clipped
186 by the device's XY offset and bitmap-bounds.
187*/
188struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400189 DeviceCM* fNext;
190 sk_sp<SkBaseDevice> fDevice;
191 SkRasterClip fClip;
192 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
193 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400194 sk_sp<SkImage> fClipImage;
195 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196
Florin Malita53f77bd2017-04-28 13:48:37 -0400197 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000198 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700199 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400200 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500201 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700202 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000203 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400204 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400205 {}
reed@google.com4b226022011-01-11 18:32:13 +0000206
mtkleinfeaadee2015-04-08 11:25:48 -0700207 void reset(const SkIRect& bounds) {
208 SkASSERT(!fPaint);
209 SkASSERT(!fNext);
210 SkASSERT(fDevice);
211 fClip.setRect(bounds);
212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213};
214
Mike Reed148b7fd2018-12-18 17:38:18 -0500215namespace {
216// Encapsulate state needed to restore from saveBehind()
217struct BackImage {
218 sk_sp<SkSpecialImage> fImage;
219 SkIPoint fLoc;
220};
221}
222
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223/* This is the record we keep for each save/restore level in the stack.
224 Since a level optionally copies the matrix and/or stack, we have pointers
225 for these fields. If the value is copied for this level, the copy is
226 stored in the ...Storage field, and the pointer points to that. If the
227 value is not copied for this level, we ignore ...Storage, and just point
228 at the corresponding value in the previous level in the stack.
229*/
230class SkCanvas::MCRec {
231public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500232 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 /* If there are any layers in the stack, this points to the top-most
234 one that is at or below this level in the stack (so we know what
235 bitmap/device to draw into from this level. This value is NOT
236 reference counted, since the real owner is either our fLayer field,
237 or a previous one in a lower level.)
238 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500239 DeviceCM* fTopLayer;
240 std::unique_ptr<BackImage> fBackImage;
241 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500242 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500243 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
Mike Reeda1361362017-03-07 09:37:29 -0500245 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700246 fLayer = nullptr;
247 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800248 fMatrix.reset();
249 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700250
reedd9544982014-09-09 18:46:22 -0700251 // don't bother initializing fNext
252 inc_rec();
253 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400254 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700255 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700256 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800257 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 // don't bother initializing fNext
260 inc_rec();
261 }
262 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700263 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 dec_rec();
265 }
mtkleinfeaadee2015-04-08 11:25:48 -0700266
267 void reset(const SkIRect& bounds) {
268 SkASSERT(fLayer);
269 SkASSERT(fDeferredSaveCount == 0);
270
271 fMatrix.reset();
272 fRasterClip.setRect(bounds);
273 fLayer->reset(bounds);
274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275};
276
Mike Reeda1361362017-03-07 09:37:29 -0500277class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278public:
Mike Reeda1361362017-03-07 09:37:29 -0500279 SkDrawIter(SkCanvas* canvas)
280 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
281 {}
reed@google.com4b226022011-01-11 18:32:13 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000284 const DeviceCM* rec = fCurrLayer;
285 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400286 fDevice = rec->fDevice.get();
287 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700289 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 return true;
291 }
292 return false;
293 }
reed@google.com4b226022011-01-11 18:32:13 +0000294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000296
Mike Reed99330ba2017-02-22 11:01:08 -0500297 SkBaseDevice* fDevice;
298
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 const DeviceCM* fCurrLayer;
301 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302};
303
Florin Malita713b8ef2017-04-28 10:57:24 -0400304#define FOR_EACH_TOP_DEVICE( code ) \
305 do { \
306 DeviceCM* layer = fMCRec->fTopLayer; \
307 while (layer) { \
308 SkBaseDevice* device = layer->fDevice.get(); \
309 if (device) { \
310 code; \
311 } \
312 layer = layer->fNext; \
313 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500314 } while (0)
315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316/////////////////////////////////////////////////////////////////////////////
317
reeddbc3cef2015-04-29 12:18:57 -0700318/**
319 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700320 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700321 */
reedd053ce92016-03-22 10:17:23 -0700322static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700323 SkImageFilter* imgf = paint.getImageFilter();
324 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700325 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700326 }
327
reedd053ce92016-03-22 10:17:23 -0700328 SkColorFilter* imgCFPtr;
329 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700330 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700331 }
reedd053ce92016-03-22 10:17:23 -0700332 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700333
334 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700335 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700336 // there is no existing paint colorfilter, so we can just return the imagefilter's
337 return imgCF;
338 }
339
340 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
341 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500342 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700343}
344
senorblanco87e066e2015-10-28 11:23:36 -0700345/**
346 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
347 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
348 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
349 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
350 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
351 * conservative "effective" bounds based on the settings in the paint... with one exception. This
352 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
353 * deliberately ignored.
354 */
355static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
356 const SkRect& rawBounds,
357 SkRect* storage) {
358 SkPaint tmpUnfiltered(paint);
359 tmpUnfiltered.setImageFilter(nullptr);
360 if (tmpUnfiltered.canComputeFastBounds()) {
361 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
362 } else {
363 return rawBounds;
364 }
365}
366
Mike Reed38992392019-07-30 10:48:15 -0400367class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368public:
senorblanco87e066e2015-10-28 11:23:36 -0700369 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
370 // paint. It's used to determine the size of the offscreen layer for filters.
371 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400372 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
373 bool skipLayerForImageFilter = false,
374 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400376 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000377 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700378 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
Mike Reed38992392019-07-30 10:48:15 -0400380 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
381 SkASSERT(!fLazyPaint.isValid());
382 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700383 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700384 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700385 fPaint = paint;
386 }
387
388 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700389 /**
390 * We implement ImageFilters for a given draw by creating a layer, then applying the
391 * imagefilter to the pixels of that layer (its backing surface/image), and then
392 * we call restore() to xfer that layer to the main canvas.
393 *
394 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
395 * 2. Generate the src pixels:
396 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
397 * return (fPaint). We then draw the primitive (using srcover) into a cleared
398 * buffer/surface.
399 * 3. Restore the layer created in #1
400 * The imagefilter is passed the buffer/surface from the layer (now filled with the
401 * src pixels of the primitive). It returns a new "filtered" buffer, which we
402 * draw onto the previous layer using the xfermode from the original paint.
403 */
Mike Reed38992392019-07-30 10:48:15 -0400404
405 SkPaint restorePaint;
406 restorePaint.setImageFilter(fPaint->refImageFilter());
407 restorePaint.setBlendMode(fPaint->getBlendMode());
408
senorblanco87e066e2015-10-28 11:23:36 -0700409 SkRect storage;
410 if (rawBounds) {
411 // Make rawBounds include all paint outsets except for those due to image filters.
412 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
413 }
Mike Reed38992392019-07-30 10:48:15 -0400414 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700415 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700416 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000417
Mike Reed38992392019-07-30 10:48:15 -0400418 // Remove the restorePaint fields from our "working" paint
419 SkASSERT(!fLazyPaint.isValid());
420 SkPaint* paint = fLazyPaint.set(origPaint);
421 paint->setImageFilter(nullptr);
422 paint->setBlendMode(SkBlendMode::kSrcOver);
423 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000424 }
425 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000426
Mike Reed38992392019-07-30 10:48:15 -0400427 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700428 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000429 fCanvas->internalRestore();
430 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000431 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000433
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434 const SkPaint& paint() const {
435 SkASSERT(fPaint);
436 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439private:
Mike Reed38992392019-07-30 10:48:15 -0400440 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400442 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000443 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700444 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445};
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447////////// macros to place around the internal draw calls //////////////////
448
Mike Reed38992392019-07-30 10:48:15 -0400449#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700450 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400451 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
452 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800453
454
Mike Reed38992392019-07-30 10:48:15 -0400455#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000456 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400457 AutoLayerForImageFilter draw(this, paint, true); \
458 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000459
Mike Reed38992392019-07-30 10:48:15 -0400460#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000461 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400462 AutoLayerForImageFilter draw(this, paint, false, bounds); \
463 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000464
Mike Reed38992392019-07-30 10:48:15 -0400465#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700466 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400467 AutoLayerForImageFilter draw(this, paint, false, bounds); \
468 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700469
Mike Reed38992392019-07-30 10:48:15 -0400470#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471
472////////////////////////////////////////////////////////////////////////////
473
msarettfbfa2582016-08-12 08:29:08 -0700474static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
475 if (bounds.isEmpty()) {
476 return SkRect::MakeEmpty();
477 }
478
479 // Expand bounds out by 1 in case we are anti-aliasing. We store the
480 // bounds as floats to enable a faster quick reject implementation.
481 SkRect dst;
482 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
483 return dst;
484}
485
mtkleinfeaadee2015-04-08 11:25:48 -0700486void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
487 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700488 fMCRec->reset(bounds);
489
490 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500491 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400492 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700493 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700494 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700495}
496
Hal Canary363a3f82018-10-04 11:04:48 -0400497void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800498 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500501 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500502 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700503 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reeda499f902015-05-01 09:34:31 -0700505 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
506 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400507 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510
halcanary96fcdcc2015-08-27 07:41:13 -0700511 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000512
reedf92c8662014-08-18 08:02:43 -0700513 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700514 // The root device and the canvas should always have the same pixel geometry
515 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800516 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700517 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518
Mike Reedc42a1cd2017-02-14 14:25:14 -0500519 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700520 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400521
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500522 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
reed@google.comcde92112011-07-06 20:00:52 +0000525SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700527 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000528{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000529 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000530
Hal Canary363a3f82018-10-04 11:04:48 -0400531 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000532}
533
reed96a857e2015-01-25 10:33:58 -0800534SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800536 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537{
538 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400539 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500540 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700541}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000542
Hal Canary363a3f82018-10-04 11:04:48 -0400543SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700544 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700545 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700546{
547 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700548
Mike Reed566e53c2017-03-10 10:49:45 -0500549 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400550 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700551}
552
Herb Derbyefe39bc2018-05-01 17:06:20 -0400553SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700555 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000556{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700558
Hal Canary363a3f82018-10-04 11:04:48 -0400559 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700560}
561
reed4a8126e2014-09-22 07:29:03 -0700562SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700563 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700564 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700565{
566 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700567
Mike Reed910ca0f2018-04-25 13:04:05 -0400568 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400569 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700570}
reed29c857d2014-09-21 10:25:07 -0700571
Mike Reed356f7c22017-01-10 11:58:39 -0500572SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
573 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700574 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
575 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500576 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700577{
578 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700579
Mike Reed910ca0f2018-04-25 13:04:05 -0400580 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400581 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582}
583
Mike Reed356f7c22017-01-10 11:58:39 -0500584SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
585
Matt Sarett31f99ce2017-04-11 08:46:01 -0400586#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
587SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
588 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
589 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
590 , fAllocator(nullptr)
591{
592 inc_canvas();
593
594 SkBitmap tmp(bitmap);
595 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400596 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400597 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400598}
599#endif
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601SkCanvas::~SkCanvas() {
602 // free up the contents of our deque
603 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 this->internalRestore(); // restore the last, since we're going away
606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 dec_canvas();
608}
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610///////////////////////////////////////////////////////////////////////////////
611
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000612void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700613 this->onFlush();
614}
615
616void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000617 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000618 if (device) {
619 device->flush();
620 }
621}
622
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500623SkSurface* SkCanvas::getSurface() const {
624 return fSurfaceBase;
625}
626
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000627SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000628 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000629 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
630}
631
senorblancoafc7cce2016-02-02 18:44:15 -0800632SkIRect SkCanvas::getTopLayerBounds() const {
633 SkBaseDevice* d = this->getTopDevice();
634 if (!d) {
635 return SkIRect::MakeEmpty();
636 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500637 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800638}
639
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000640SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000642 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400644 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645}
646
Florin Malita0ed3b642017-01-13 16:56:38 +0000647SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400648 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000649}
650
Mike Reed353196f2017-07-21 11:01:18 -0400651bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000652 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400653 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000654}
655
Mike Reed353196f2017-07-21 11:01:18 -0400656bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
657 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400658}
659
660bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
661 SkPixmap pm;
662 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
663}
664
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000665bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400666 SkPixmap pm;
667 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700668 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000669 }
670 return false;
671}
672
Matt Sarett03dd6d52017-01-23 12:15:09 -0500673bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000675 SkBaseDevice* device = this->getDevice();
676 if (!device) {
677 return false;
678 }
679
Matt Sarett03dd6d52017-01-23 12:15:09 -0500680 // This check gives us an early out and prevents generation ID churn on the surface.
681 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
682 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400683 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500684 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000686
Matt Sarett03dd6d52017-01-23 12:15:09 -0500687 // Tell our owning surface to bump its generation ID.
688 const bool completeOverwrite =
689 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700690 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700691
Matt Sarett03dd6d52017-01-23 12:15:09 -0500692 // This can still fail, most notably in the case of a invalid color type or alpha type
693 // conversion. We could pull those checks into this function and avoid the unnecessary
694 // generation ID bump. But then we would be performing those checks twice, since they
695 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400696 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000697}
reed@google.com51df9e32010-12-23 19:29:18 +0000698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699//////////////////////////////////////////////////////////////////////////////
700
reed2ff1fce2014-12-11 07:07:37 -0800701void SkCanvas::checkForDeferredSave() {
702 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800703 this->doSave();
704 }
705}
706
reedf0090cb2014-11-26 08:55:51 -0800707int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800708#ifdef SK_DEBUG
709 int count = 0;
710 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
711 for (;;) {
712 const MCRec* rec = (const MCRec*)iter.next();
713 if (!rec) {
714 break;
715 }
716 count += 1 + rec->fDeferredSaveCount;
717 }
718 SkASSERT(count == fSaveCount);
719#endif
720 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800721}
722
723int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800724 fSaveCount += 1;
725 fMCRec->fDeferredSaveCount += 1;
726 return this->getSaveCount() - 1; // return our prev value
727}
728
729void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800730 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700731
732 SkASSERT(fMCRec->fDeferredSaveCount > 0);
733 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800734 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800735}
736
Mike Reed00a97642020-01-25 18:42:23 -0500737int SkCanvas::experimental_saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500738 // TODO: add a virtual for this, and update clients (e.g. chrome)
739 int n = this->save();
Mike Reed46f5c5f2020-02-20 15:42:29 -0500740 this->concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500741 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500742 return n;
743}
744
Mike Reed00a97642020-01-25 18:42:23 -0500745int SkCanvas::experimental_saveCamera(const SkScalar projection[16],
746 const SkScalar camera[16]) {
747 SkM44 proj, cam;
748 proj.setColMajor(projection);
749 cam.setColMajor(camera);
750 return this->experimental_saveCamera(proj, cam);
751}
752
reedf0090cb2014-11-26 08:55:51 -0800753void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800754 if (fMCRec->fDeferredSaveCount > 0) {
755 SkASSERT(fSaveCount > 1);
756 fSaveCount -= 1;
757 fMCRec->fDeferredSaveCount -= 1;
758 } else {
759 // check for underflow
760 if (fMCStack.count() > 1) {
761 this->willRestore();
762 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700763 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800764 this->internalRestore();
765 this->didRestore();
766 }
reedf0090cb2014-11-26 08:55:51 -0800767 }
768}
769
770void SkCanvas::restoreToCount(int count) {
771 // sanity check
772 if (count < 1) {
773 count = 1;
774 }
mtkleinf0f14112014-12-12 08:46:25 -0800775
reedf0090cb2014-11-26 08:55:51 -0800776 int n = this->getSaveCount() - count;
777 for (int i = 0; i < n; ++i) {
778 this->restore();
779 }
780}
781
reed2ff1fce2014-12-11 07:07:37 -0800782void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700784 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000786
Mike Reedc42a1cd2017-02-14 14:25:14 -0500787 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788}
789
reed4960eee2015-12-18 07:09:18 -0800790bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400791 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792}
793
reed4960eee2015-12-18 07:09:18 -0800794bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700795 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400796 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
797 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
798 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
799 // filter does not require a decomposed CTM matrix, the filter space and device space are the
800 // same. When it has been decomposed, we want the original image filter node to process the
801 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
802 // longer the original filter, but has the remainder matrix baked into it, and passing in the
803 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
804 // to the original filter node (barring inflation from consecutive calls to mapRect). While
805 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
806 // passing getDeviceClipBounds() to 'imageFilter' is correct.
807 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
808 // be important to more accurately calculate the clip bounds in the layer space for the original
809 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500810 SkIRect clipBounds = this->getDeviceClipBounds();
811 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000812 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000813 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000814
reed96e657d2015-03-10 17:30:07 -0700815 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
816
Robert Phillips12078432018-05-17 11:17:39 -0400817 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
818 // If the image filter DAG affects transparent black then we will need to render
819 // out to the clip bounds
820 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000821 }
Robert Phillips12078432018-05-17 11:17:39 -0400822
823 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700824 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700826 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400827 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400829 inputSaveLayerBounds = clipBounds;
830 }
831
832 if (imageFilter) {
833 // expand the clip bounds by the image filter DAG to include extra content that might
834 // be required by the image filters.
835 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
836 SkImageFilter::kReverse_MapDirection,
837 &inputSaveLayerBounds);
838 }
839
840 SkIRect clippedSaveLayerBounds;
841 if (bounds) {
842 // For better or for worse, user bounds currently act as a hard clip on the layer's
843 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
844 clippedSaveLayerBounds = inputSaveLayerBounds;
845 } else {
846 // If there are no user bounds, we don't want to artificially restrict the resulting
847 // layer bounds, so allow the expanded clip bounds free reign.
848 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800850
851 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400852 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800853 if (BoundsAffectsClip(saveLayerFlags)) {
854 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
855 fMCRec->fRasterClip.setEmpty();
856 fDeviceClipBounds.setEmpty();
857 }
858 return false;
859 }
Robert Phillips12078432018-05-17 11:17:39 -0400860 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861
reed4960eee2015-12-18 07:09:18 -0800862 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700863 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400864 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
865 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000866 }
867
868 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400869 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870 }
Robert Phillips12078432018-05-17 11:17:39 -0400871
junov@chromium.orga907ac32012-02-24 21:54:07 +0000872 return true;
873}
874
reed4960eee2015-12-18 07:09:18 -0800875int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
876 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000877}
878
Cary Clarke041e312018-03-06 13:00:52 -0500879int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700880 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400881 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
882 // no need for the layer (or any of the draws until the matching restore()
883 this->save();
884 this->clipRect({0,0,0,0});
885 } else {
886 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
887 fSaveCount += 1;
888 this->internalSaveLayer(rec, strategy);
889 }
reed4960eee2015-12-18 07:09:18 -0800890 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800891}
892
Mike Reed148b7fd2018-12-18 17:38:18 -0500893int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
894 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
895 // Assuming clips never expand, if the request bounds is outside of the current clip
896 // there is no need to copy/restore the area, so just devolve back to a regular save.
897 this->save();
898 } else {
899 bool doTheWork = this->onDoSaveBehind(bounds);
900 fSaveCount += 1;
901 this->internalSave();
902 if (doTheWork) {
903 this->internalSaveBehind(bounds);
904 }
905 }
906 return this->getSaveCount() - 1;
907}
908
reeda2217ef2016-07-20 06:04:34 -0700909void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500910 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500911 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400912 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
913 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400914 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500915 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
916 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400917 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400918 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
919 // This means that we only have to copy a dst-sized block of pixels out of src and translate
920 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400921 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
922 dstOrigin.y() - src->getOrigin().y(),
923 dst->width(), dst->height());
924 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400925 return;
926 }
927
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400928 auto special = src->snapSpecial(snapBounds);
929 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400930 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
931 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400932 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
933 }
934 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400935 }
936
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400937 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
938 // by the backdrop filter.
939 SkMatrix toRoot, layerMatrix;
940 SkSize scale;
941 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
942 toRoot = SkMatrix::I();
943 layerMatrix = ctm;
944 } else if (ctm.decomposeScale(&scale, &toRoot)) {
945 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
946 } else {
947 // Perspective, for now, do no scaling of the layer itself.
948 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
949 // the matrix, e.g. based on the midpoint of the near/far planes?
950 toRoot = ctm;
951 layerMatrix = SkMatrix::I();
952 }
953
954 // We have to map the dst bounds from the root space into the layer space where filtering will
955 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
956 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
957 // is a safe, conservative estimate.
958 SkMatrix fromRoot;
959 if (!toRoot.invert(&fromRoot)) {
960 return;
961 }
962
963 // This represents what the backdrop filter needs to produce in the layer space, and is sized
964 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
965 SkIRect layerTargetBounds = fromRoot.mapRect(
966 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
967 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
968 // require some extra input pixels.
969 SkIRect layerInputBounds = filter->filterBounds(
970 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
971 &layerTargetBounds);
972
973 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400974 // be the conservative contents required to fill a layerInputBounds-sized surface with the
975 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400976 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
977 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
978 if (!backdropBounds.intersect(srcDevRect)) {
979 return;
980 }
981
982 auto special = src->snapSpecial(backdropBounds);
983 if (!special) {
984 return;
985 }
986
987 SkColorType colorType = src->imageInfo().colorType();
988 if (colorType == kUnknown_SkColorType) {
989 colorType = kRGBA_8888_SkColorType;
990 }
991 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400992
993 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400994 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400995 // Drawing the temporary and final filtered image requires a higher filter quality if the
996 // 'toRoot' transformation is not identity, in order to minimize the impact on already
997 // rendered edges/content.
998 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
999 p.setFilterQuality(kHigh_SkFilterQuality);
1000
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001001 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1002 // and stored in a temporary surface, which is then used as the input to the actual filter.
1003 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1004 if (!tmpSurface) {
1005 return;
1006 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001007
1008 auto tmpCanvas = tmpSurface->getCanvas();
1009 tmpCanvas->clear(SK_ColorTRANSPARENT);
1010 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1011 // space, then maps from root space into the layer space, then maps it so the input layer's
1012 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1013 // performed on backdropBounds.
1014 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1015 tmpCanvas->concat(fromRoot);
1016 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001017
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001018 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1019 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1020 special = tmpSurface->makeImageSnapshot();
1021 } else {
1022 // Since there is no extra transform that was done, update the input bounds to reflect
1023 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1024 // was equal to backdropBounds before it was made relative to the src device and cropped.
1025 // When we use the original snapped image directly, just map the update backdrop bounds
1026 // back into the shared layer space
1027 layerInputBounds = backdropBounds;
1028 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001029
1030 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1031 // draw will be 1-1 so there is no need to increase filter quality.
1032 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001033 }
1034
1035 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1036 // layer space. This has to further offset everything so that filter evaluation thinks the
1037 // source image's top left corner is (0, 0).
1038 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1039 // this can be simplified.
1040 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1041 SkMatrix filterCTM = layerMatrix;
1042 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1043 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1044
1045 SkIPoint offset;
1046 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001047 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001048 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1049 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1050 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1051 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001052 offset += layerInputBounds.topLeft();
1053
1054 // Manually setting the device's CTM requires accounting for the device's origin.
1055 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001056 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001057 // a global CTM instead of a device CTM.
1058 SkMatrix dstCTM = toRoot;
1059 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001060 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001061
1062 // And because devices don't have a special-image draw function that supports arbitrary
1063 // matrices, we are abusing the asImage() functionality here...
1064 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001065 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001066 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001067 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001068 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1069 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001070 }
robertphillips7354a4b2015-12-16 05:08:27 -08001071}
reed70ee31b2015-12-10 13:44:45 -08001072
Mike Kleine083f7c2018-02-07 12:54:27 -05001073static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001074 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001075 if (prev.bytesPerPixel() <= 4 &&
1076 prev.colorType() != kRGBA_8888_SkColorType &&
1077 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001078 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1079 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1080 ct = kN32_SkColorType;
1081 }
1082 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001083}
1084
reed4960eee2015-12-18 07:09:18 -08001085void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001086 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001087 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001088 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1089
Michael Ludwigeced98b2020-03-03 10:39:41 -05001090 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1091 // saveLayer ignores mask filters, so force it to null
1092 if (paint.get() && paint->getMaskFilter()) {
1093 paint.writable()->setMaskFilter(nullptr);
1094 }
1095
Mike Reed5532c2a2019-02-23 12:00:32 -05001096 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1097 // regardless of any hint-rect from the caller. skbug.com/8783
1098 if (rec.fBackdrop) {
1099 bounds = nullptr;
1100 }
1101
Michael Ludwigeced98b2020-03-03 10:39:41 -05001102 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001103 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001104 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001105
reed8c30a812016-04-20 16:36:51 -07001106 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001107 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1108 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1109 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001110 *
1111 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001112 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1113 * if necessary.
1114 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1115 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001116 * 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 -04001117 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001118 * of the original imagefilter, and draw that (via drawSprite)
1119 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1120 *
1121 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1122 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1123 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001124 if (imageFilter) {
1125 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001126 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1127 &modifiedCTM);
1128 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001129 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001130 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001131 modifiedRec = fMCRec;
1132 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001133 imageFilter = modifiedFilter.get();
1134 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001135 }
1136 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1137 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001138 }
reed8c30a812016-04-20 16:36:51 -07001139
junov@chromium.orga907ac32012-02-24 21:54:07 +00001140 // do this before we create the layer. We don't call the public save() since
1141 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001142 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001143
junov@chromium.orga907ac32012-02-24 21:54:07 +00001144 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001145 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001146 if (modifiedRec) {
1147 // In this case there will be no layer in which to stash the matrix so we need to
1148 // revert the prior MCRec to its earlier state.
1149 modifiedRec->fMatrix = stashedMatrix;
1150 }
reed2ff1fce2014-12-11 07:07:37 -08001151 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 }
1153
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001154 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1155 // the clipRectBounds() call above?
1156 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001157 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001158 }
1159
reed8dc0ccb2015-03-20 06:32:52 -07001160 SkPixelGeometry geo = fProps.pixelGeometry();
1161 if (paint) {
reed76033be2015-03-14 10:54:31 -07001162 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001163 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001164 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001165 }
1166 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167
robertphillips5139e502016-07-19 05:10:40 -07001168 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001169 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001170 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001171 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001172 }
reedb2db8982014-11-13 12:41:02 -08001173
Mike Kleine083f7c2018-02-07 12:54:27 -05001174 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001175 if (rec.fSaveLayerFlags & kF16ColorType) {
1176 info = info.makeColorType(kRGBA_F16_SkColorType);
1177 }
reed129ed1c2016-02-22 06:42:31 -08001178
Hal Canary704cd322016-11-07 14:13:52 -05001179 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001180 {
Florin Malita4571e492019-07-16 10:25:58 -04001181 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001182 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001183 const bool trackCoverage =
1184 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001185 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001186 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001187 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001188 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1189 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001190 return;
reed61f501f2015-04-29 08:34:00 -07001191 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001192 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001193 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194
Mike Reedb43a3e02017-02-11 10:18:58 -05001195 // only have a "next" if this new layer doesn't affect the clip (rare)
1196 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 fMCRec->fLayer = layer;
1198 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001199
Mike Reedc61abee2017-02-28 17:45:27 -05001200 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001201 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001202 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001203 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001204
Mike Reedc42a1cd2017-02-14 14:25:14 -05001205 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1206
1207 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1208 if (layer->fNext) {
1209 // need to punch a hole in the previous device, so we don't draw there, given that
1210 // the new top-layer will allow drawing to happen "below" it.
1211 SkRegion hole(ir);
1212 do {
1213 layer = layer->fNext;
1214 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1215 } while (layer->fNext);
1216 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217}
1218
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001219int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001220 if (0xFF == alpha) {
1221 return this->saveLayer(bounds, nullptr);
1222 } else {
1223 SkPaint tmpPaint;
1224 tmpPaint.setAlpha(alpha);
1225 return this->saveLayer(bounds, &tmpPaint);
1226 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001227}
1228
Mike Reed148b7fd2018-12-18 17:38:18 -05001229void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001230 SkBaseDevice* device = this->getTopDevice();
1231 if (nullptr == device) { // Do we still need this check???
1232 return;
1233 }
1234
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001235 // Map the local bounds into the top device's coordinate space (this is not
1236 // necessarily the full global CTM transform).
1237 SkIRect devBounds;
1238 if (localBounds) {
1239 SkRect tmp;
1240 device->localToDevice().mapRect(&tmp, *localBounds);
1241 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1242 devBounds.setEmpty();
1243 }
1244 } else {
1245 devBounds = device->devClipBounds();
1246 }
1247 if (devBounds.isEmpty()) {
1248 return;
1249 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001250
Michael Ludwigac352122019-08-28 21:03:05 +00001251 // This is getting the special image from the current device, which is then drawn into (both by
1252 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1253 // own device, we need to explicitly copy the back image contents so that its original content
1254 // is available when we splat it back later during restore.
1255 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001256 if (!backImage) {
1257 return;
1258 }
1259
1260 // we really need the save, so we can wack the fMCRec
1261 this->checkForDeferredSave();
1262
1263 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1264
1265 SkPaint paint;
1266 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001267 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001268}
1269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270void SkCanvas::internalRestore() {
1271 SkASSERT(fMCStack.count() != 0);
1272
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001273 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 DeviceCM* layer = fMCRec->fLayer; // may be null
1275 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001276 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277
Mike Reed148b7fd2018-12-18 17:38:18 -05001278 // move this out before we do the actual restore
1279 auto backImage = std::move(fMCRec->fBackImage);
1280
Mike Reedb18e74d2020-01-16 13:58:22 -05001281 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1282 fCameraStack.pop_back();
1283 }
1284
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 // now do the normal restore()
1286 fMCRec->~MCRec(); // balanced in save()
1287 fMCStack.pop_back();
1288 fMCRec = (MCRec*)fMCStack.back();
1289
Mike Reedc42a1cd2017-02-14 14:25:14 -05001290 if (fMCRec) {
1291 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1292 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001293
Mike Reed148b7fd2018-12-18 17:38:18 -05001294 if (backImage) {
1295 SkPaint paint;
1296 paint.setBlendMode(SkBlendMode::kDstOver);
1297 const int x = backImage->fLoc.x();
1298 const int y = backImage->fLoc.y();
1299 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1300 nullptr, SkMatrix::I());
1301 }
1302
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1304 since if we're being recorded, we don't want to record this (the
1305 recorder will have already recorded the restore).
1306 */
bsalomon49f085d2014-09-05 13:34:00 -07001307 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001308 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001309 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001310 // At this point, 'layer' has been removed from the device stack, so the devices that
1311 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1312 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001313 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001314 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001315 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001316 delete layer;
reedb679ca82015-04-07 04:40:48 -07001317 } else {
1318 // we're at the root
reeda499f902015-05-01 09:34:31 -07001319 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001320 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001321 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001323 }
msarettfbfa2582016-08-12 08:29:08 -07001324
1325 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001326 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001327 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1328 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329}
1330
reede8f30622016-03-23 18:59:25 -07001331sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001332 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001333 props = &fProps;
1334 }
1335 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001336}
1337
reede8f30622016-03-23 18:59:25 -07001338sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001339 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001340 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001341}
1342
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001343SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001344 return this->onImageInfo();
1345}
1346
1347SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001348 SkBaseDevice* dev = this->getDevice();
1349 if (dev) {
1350 return dev->imageInfo();
1351 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001352 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001353 }
1354}
1355
brianosman898235c2016-04-06 07:38:23 -07001356bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001357 return this->onGetProps(props);
1358}
1359
1360bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001361 SkBaseDevice* dev = this->getDevice();
1362 if (dev) {
1363 if (props) {
1364 *props = fProps;
1365 }
1366 return true;
1367 } else {
1368 return false;
1369 }
1370}
1371
reed6ceeebd2016-03-09 14:26:26 -08001372bool SkCanvas::peekPixels(SkPixmap* pmap) {
1373 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001374}
1375
reed884e97c2015-05-26 11:31:54 -07001376bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001378 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001379}
1380
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001381void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001382 SkPixmap pmap;
1383 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001384 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001385 }
1386 if (info) {
1387 *info = pmap.info();
1388 }
1389 if (rowBytes) {
1390 *rowBytes = pmap.rowBytes();
1391 }
1392 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001393 // If the caller requested the origin, they presumably are expecting the returned pixels to
1394 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1395 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1396 // instead of an origin, just don't expose the pixels in that case. Note that this means
1397 // that layers with complex coordinate spaces can still report their pixels if the caller
1398 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1399 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1400 *origin = this->getTopDevice()->getOrigin();
1401 } else {
1402 return nullptr;
1403 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001404 }
reed884e97c2015-05-26 11:31:54 -07001405 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001406}
1407
reed884e97c2015-05-26 11:31:54 -07001408bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001409 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001410 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001411}
1412
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414
Mike Reed8bcd1282019-03-13 16:51:54 -04001415// In our current design/features, we should never have a layer (src) in a different colorspace
1416// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1417// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1418// colorspace.
1419static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1420 SkASSERT(src == dst);
1421}
1422
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001423void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001424 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001426 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 paint = &tmp;
1428 }
reed@google.com4b226022011-01-11 18:32:13 +00001429
Mike Reed38992392019-07-30 10:48:15 -04001430 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001431
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001433 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001434 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1435 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001436 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001437 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001438 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1439 // so it should always be possible to use the relative origin. Once drawDevice() and
1440 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1441 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001442 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001443 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1444 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001445 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1446 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001447 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1448 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001449 }
reed@google.com76dd2772012-01-05 21:15:07 +00001450 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001451 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 }
reeda2217ef2016-07-20 06:04:34 -07001454
Mike Reed38992392019-07-30 10:48:15 -04001455 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
reed32704672015-12-16 08:27:10 -08001458/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001459
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001460void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001461 if (dx || dy) {
1462 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001463 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001464
reedfe69b502016-09-12 06:31:48 -07001465 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001466 // However, if either is non-finite, we might still complicate the matrix type,
1467 // so we still have to compute this.
1468 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001469
Mike Reedc42a1cd2017-02-14 14:25:14 -05001470 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001471
reedfe69b502016-09-12 06:31:48 -07001472 this->didTranslate(dx,dy);
1473 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001476void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001477 if (sx != 1 || sy != 1) {
1478 this->checkForDeferredSave();
1479 fMCRec->fMatrix.preScale(sx, sy);
1480
1481 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1482 // but pre-scaling by a non-finite does change it, so we have to recompute.
1483 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1484
1485 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1486
1487 this->didScale(sx, sy);
1488 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001491void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001492 SkMatrix m;
1493 m.setRotate(degrees);
1494 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495}
1496
bungeman7438bfc2016-07-12 15:01:19 -07001497void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1498 SkMatrix m;
1499 m.setRotate(degrees, px, py);
1500 this->concat(m);
1501}
1502
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001503void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001504 SkMatrix m;
1505 m.setSkew(sx, sy);
1506 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001507}
1508
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001509void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001510 if (matrix.isIdentity()) {
1511 return;
1512 }
1513
reed2ff1fce2014-12-11 07:07:37 -08001514 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001515 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001516
msarett9637ea92016-08-18 14:03:30 -07001517 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001518
Mike Reed7627fa52017-02-08 10:07:53 -05001519 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001520
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001521 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001522}
1523
Mike Reed46f5c5f2020-02-20 15:42:29 -05001524void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001525 this->checkForDeferredSave();
1526
Mike Reed46f5c5f2020-02-20 15:42:29 -05001527 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001528
1529 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1530
1531 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1532
Mike Reed46f5c5f2020-02-20 15:42:29 -05001533 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001534}
Mike Reed064c7f92020-01-08 17:33:04 -05001535
Mike Reed46f5c5f2020-02-20 15:42:29 -05001536void SkCanvas::concat44(const SkM44& m) {
1537 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001538}
1539
reed8c30a812016-04-20 16:36:51 -07001540void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001541 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001542 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001543
Mike Reedc42a1cd2017-02-14 14:25:14 -05001544 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001545}
1546
1547void SkCanvas::setMatrix(const SkMatrix& matrix) {
1548 this->checkForDeferredSave();
1549 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001550 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551}
1552
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001554 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555}
1556
1557//////////////////////////////////////////////////////////////////////////////
1558
Mike Reedc1f77742016-12-09 09:00:50 -05001559void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001560 if (!rect.isFinite()) {
1561 return;
1562 }
reed2ff1fce2014-12-11 07:07:37 -08001563 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001564 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1565 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001566}
1567
Mike Reedc1f77742016-12-09 09:00:50 -05001568void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001569 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001570
Mike Reed7627fa52017-02-08 10:07:53 -05001571 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001572
reedc64eff52015-11-21 12:39:45 -08001573 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001574 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1575 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001576 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577}
1578
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001579void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1580 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001581 if (fClipRestrictionRect.isEmpty()) {
1582 // we notify the device, but we *dont* resolve deferred saves (since we're just
1583 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001584 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001585 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001586 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001587 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001588 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001589 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001590 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1591 }
1592}
1593
Mike Reedc1f77742016-12-09 09:00:50 -05001594void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001595 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001597 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1599 } else {
1600 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001601 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001602}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001603
Mike Reedc1f77742016-12-09 09:00:50 -05001604void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001605 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001606
Brian Salomona3b45d42016-10-03 11:36:16 -04001607 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001608
Mike Reed7627fa52017-02-08 10:07:53 -05001609 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001610
Mike Reed20800c82017-11-15 16:09:04 -05001611 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1612 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001613 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001614}
1615
Mike Reedc1f77742016-12-09 09:00:50 -05001616void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001617 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001618 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001619
1620 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1621 SkRect r;
1622 if (path.isRect(&r)) {
1623 this->onClipRect(r, op, edgeStyle);
1624 return;
1625 }
1626 SkRRect rrect;
1627 if (path.isOval(&r)) {
1628 rrect.setOval(r);
1629 this->onClipRRect(rrect, op, edgeStyle);
1630 return;
1631 }
1632 if (path.isRRect(&rrect)) {
1633 this->onClipRRect(rrect, op, edgeStyle);
1634 return;
1635 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001636 }
robertphillips39f05382015-11-24 09:30:12 -08001637
1638 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001639}
1640
Mike Reedc1f77742016-12-09 09:00:50 -05001641void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001642 AutoValidateClip avc(this);
1643
Brian Salomona3b45d42016-10-03 11:36:16 -04001644 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001645
Mike Reed7627fa52017-02-08 10:07:53 -05001646 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647
Brian Salomona3b45d42016-10-03 11:36:16 -04001648 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001649 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001650 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001651 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652}
1653
Mike Reed121c2af2020-03-10 14:02:56 -04001654void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1655 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001656 if (sh->isOpaque()) {
1657 if (op == SkClipOp::kIntersect) {
1658 // we don't occlude anything, so skip this call
1659 } else {
1660 SkASSERT(op == SkClipOp::kDifference);
1661 // we occlude everything, so set the clip to empty
1662 this->clipRect({0,0,0,0});
1663 }
1664 } else {
1665 this->onClipShader(std::move(sh), op);
1666 }
Mike Reed121c2af2020-03-10 14:02:56 -04001667 }
1668}
1669
1670void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1671 AutoValidateClip avc(this);
1672
1673 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1674
1675 // we don't know how to mutate our conservative bounds, so we don't
1676}
1677
Mike Reedc1f77742016-12-09 09:00:50 -05001678void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001679 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001680 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001681}
1682
Mike Reedc1f77742016-12-09 09:00:50 -05001683void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001684 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001685
reed@google.com5c3d1472011-02-22 19:12:23 +00001686 AutoValidateClip avc(this);
1687
Mike Reed20800c82017-11-15 16:09:04 -05001688 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001689 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001690}
1691
reed@google.com819c9212011-02-23 18:56:55 +00001692#ifdef SK_DEBUG
1693void SkCanvas::validateClip() const {
1694 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001695 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001696 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001697 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001698 return;
1699 }
reed@google.com819c9212011-02-23 18:56:55 +00001700}
1701#endif
1702
Mike Reeda1361362017-03-07 09:37:29 -05001703bool SkCanvas::androidFramework_isClipAA() const {
1704 bool containsAA = false;
1705
1706 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1707
1708 return containsAA;
1709}
1710
1711class RgnAccumulator {
1712 SkRegion* fRgn;
1713public:
1714 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1715 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1716 SkIPoint origin = device->getOrigin();
1717 if (origin.x() | origin.y()) {
1718 rgn->translate(origin.x(), origin.y());
1719 }
1720 fRgn->op(*rgn, SkRegion::kUnion_Op);
1721 }
1722};
1723
1724void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1725 RgnAccumulator accum(rgn);
1726 SkRegion tmp;
1727
1728 rgn->setEmpty();
1729 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001730}
1731
reed@google.com5c3d1472011-02-22 19:12:23 +00001732///////////////////////////////////////////////////////////////////////////////
1733
reed@google.com754de5f2014-02-24 19:38:20 +00001734bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001735 return fMCRec->fRasterClip.isEmpty();
1736
1737 // TODO: should we only use the conservative answer in a recording canvas?
1738#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001739 SkBaseDevice* dev = this->getTopDevice();
1740 // if no device we return true
1741 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001742#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001743}
1744
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001745bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001746 SkBaseDevice* dev = this->getTopDevice();
1747 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001748 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001749}
1750
msarettfbfa2582016-08-12 08:29:08 -07001751static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1752#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1753 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1754 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1755 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1756 return 0xF != _mm_movemask_ps(mask);
1757#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1758 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1759 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1760 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1761 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1762#else
1763 SkRect devRectAsRect;
1764 SkRect devClipAsRect;
1765 devRect.store(&devRectAsRect.fLeft);
1766 devClip.store(&devClipAsRect.fLeft);
1767 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1768#endif
1769}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001770
msarettfbfa2582016-08-12 08:29:08 -07001771// It's important for this function to not be inlined. Otherwise the compiler will share code
1772// between the fast path and the slow path, resulting in two slow paths.
1773static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1774 const SkMatrix& matrix) {
1775 SkRect deviceRect;
1776 matrix.mapRect(&deviceRect, src);
1777 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1778}
1779
1780bool SkCanvas::quickReject(const SkRect& src) const {
1781#ifdef SK_DEBUG
1782 // Verify that fDeviceClipBounds are set properly.
1783 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001784 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001785 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001786 } else {
msarettfbfa2582016-08-12 08:29:08 -07001787 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788 }
msarettfbfa2582016-08-12 08:29:08 -07001789
msarett9637ea92016-08-18 14:03:30 -07001790 // Verify that fIsScaleTranslate is set properly.
1791 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001792#endif
1793
msarett9637ea92016-08-18 14:03:30 -07001794 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001795 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1796 }
1797
1798 // We inline the implementation of mapScaleTranslate() for the fast path.
1799 float sx = fMCRec->fMatrix.getScaleX();
1800 float sy = fMCRec->fMatrix.getScaleY();
1801 float tx = fMCRec->fMatrix.getTranslateX();
1802 float ty = fMCRec->fMatrix.getTranslateY();
1803 Sk4f scale(sx, sy, sx, sy);
1804 Sk4f trans(tx, ty, tx, ty);
1805
1806 // Apply matrix.
1807 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1808
1809 // Make sure left < right, top < bottom.
1810 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1811 Sk4f min = Sk4f::Min(ltrb, rblt);
1812 Sk4f max = Sk4f::Max(ltrb, rblt);
1813 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1814 // ARM this sequence generates the fastest (a single instruction).
1815 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1816
1817 // Check if the device rect is NaN or outside the clip.
1818 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819}
1820
reed@google.com3b3e8952012-08-16 20:53:31 +00001821bool SkCanvas::quickReject(const SkPath& path) const {
1822 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823}
1824
Mike Klein83c8dd92017-11-28 17:08:45 -05001825SkRect SkCanvas::getLocalClipBounds() const {
1826 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001827 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001828 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 }
1830
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001831 SkMatrix inverse;
1832 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001833 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001834 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001835 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836
Mike Reed42e8c532017-01-23 14:09:13 -05001837 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001838 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001839 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001840
Mike Reedb57b9312018-04-23 12:12:54 -04001841 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001842 inverse.mapRect(&bounds, r);
1843 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844}
1845
Mike Klein83c8dd92017-11-28 17:08:45 -05001846SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001847 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001848}
1849
Mike Reedb18e74d2020-01-16 13:58:22 -05001850///////////////////////////////////////////////////////////////////////
1851
1852SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1853 : fMCRec(owner)
1854 , fCamera(camera)
1855{
1856 // assumes the mcrec has already been concatenated with the camera
1857 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1858 fInvPostCamera.setIdentity();
1859 }
1860}
1861
Mike Reed403c8072020-01-08 10:40:39 -05001862SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001863 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864}
1865
Mike Reed46f5c5f2020-02-20 15:42:29 -05001866SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001867 return fMCRec->fMatrix;
1868}
1869
Mike Reedc43f2a02020-01-16 14:54:34 -05001870SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001871 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001872 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001873 } else {
1874 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001875 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001876 }
1877}
1878
Mike Reedc43f2a02020-01-16 14:54:34 -05001879SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001880 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001881 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001882 } else {
1883 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001884 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001885 }
1886}
1887
Mike Reed46f5c5f2020-02-20 15:42:29 -05001888void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1889 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001890}
1891
1892void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1893 this->experimental_getLocalToWorld().getColMajor(colMajor);
1894}
1895
1896void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1897 this->experimental_getLocalToCamera().getColMajor(colMajor);
1898}
1899
Brian Osman11052242016-10-27 14:47:55 -04001900GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001901 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001902 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001903}
1904
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001905GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001906 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001907 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001908}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001909
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001910void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1911 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001912 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001913 if (outer.isEmpty()) {
1914 return;
1915 }
1916 if (inner.isEmpty()) {
1917 this->drawRRect(outer, paint);
1918 return;
1919 }
1920
1921 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001922 // be able to return ...
1923 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001924 //
1925 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001926 if (!outer.getBounds().contains(inner.getBounds())) {
1927 return;
1928 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001929
1930 this->onDrawDRRect(outer, inner, paint);
1931}
1932
reed41af9662015-01-05 07:49:08 -08001933void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001934 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001935 this->onDrawPaint(paint);
1936}
1937
1938void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001939 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001940 // To avoid redundant logic in our culling code and various backends, we always sort rects
1941 // before passing them along.
1942 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001943}
1944
Mike Reedd5674082019-04-19 15:00:47 -04001945void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1946 TRACE_EVENT0("skia", TRACE_FUNC);
1947 this->onDrawBehind(paint);
1948}
1949
msarettdca352e2016-08-26 06:37:45 -07001950void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001951 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001952 if (region.isEmpty()) {
1953 return;
1954 }
1955
1956 if (region.isRect()) {
1957 return this->drawIRect(region.getBounds(), paint);
1958 }
1959
1960 this->onDrawRegion(region, paint);
1961}
1962
reed41af9662015-01-05 07:49:08 -08001963void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001964 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001965 // To avoid redundant logic in our culling code and various backends, we always sort rects
1966 // before passing them along.
1967 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001968}
1969
1970void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001971 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001972 this->onDrawRRect(rrect, paint);
1973}
1974
1975void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001976 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001977 this->onDrawPoints(mode, count, pts, paint);
1978}
1979
Mike Reede88a1cb2017-03-17 09:50:46 -04001980void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1981 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001982 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001983}
1984
1985void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001986 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001987 RETURN_ON_NULL(vertices);
Brian Osmanf11e3312020-03-24 14:57:38 -04001988
1989 SkVertices::Info info;
1990 vertices->getInfo(&info);
1991
Mike Reed5caf9352020-03-02 14:57:09 -05001992 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Brian Osmanf11e3312020-03-24 14:57:38 -04001993 SkASSERT(info.fMode != SkVertices::kTriangleFan_VertexMode);
1994
1995 // If the vertices contain custom attributes, ensure they line up with the paint's shader
1996 const SkRuntimeEffect* effect =
1997 paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
1998 if (info.fPerVertexDataCount != (effect ? effect->varyingCount() : 0)) {
1999 return;
2000 }
2001
Mike Reed5caf9352020-03-02 14:57:09 -05002002 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08002003}
2004
2005void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002006 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08002007 this->onDrawPath(path, paint);
2008}
2009
reeda85d4d02015-05-06 12:56:48 -07002010void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002011 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002012 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07002013 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08002014}
2015
Mike Reedc4e31092018-01-30 11:15:27 -05002016// Returns true if the rect can be "filled" : non-empty and finite
2017static bool fillable(const SkRect& r) {
2018 SkScalar w = r.width();
2019 SkScalar h = r.height();
2020 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
2021}
2022
reede47829b2015-08-06 10:02:53 -07002023void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2024 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002025 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002026 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002027 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002028 return;
2029 }
2030 this->onDrawImageRect(image, &src, dst, paint, constraint);
2031}
reed41af9662015-01-05 07:49:08 -08002032
reed84984ef2015-07-17 07:09:43 -07002033void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2034 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002035 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002036 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002037}
2038
Brian Salomonf08002c2018-10-26 16:15:46 -04002039void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002040 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002041 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002042 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002043}
reede47829b2015-08-06 10:02:53 -07002044
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002045namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002046class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002047public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002048 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2049 if (!origPaint) {
2050 return;
2051 }
2052 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2053 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2054 }
2055 if (origPaint->getMaskFilter()) {
2056 fPaint.writable()->setMaskFilter(nullptr);
2057 }
2058 if (origPaint->isAntiAlias()) {
2059 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002060 }
2061 }
2062
2063 const SkPaint* get() const {
2064 return fPaint;
2065 }
2066
2067private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002068 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002069};
2070} // namespace
2071
reed4c21dc52015-06-25 12:32:03 -07002072void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2073 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002074 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002075 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002076 if (dst.isEmpty()) {
2077 return;
2078 }
msarett552bca92016-08-03 06:53:26 -07002079 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002080 LatticePaint latticePaint(paint);
2081 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002082 } else {
reede47829b2015-08-06 10:02:53 -07002083 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002084 }
reed4c21dc52015-06-25 12:32:03 -07002085}
2086
msarett16882062016-08-16 09:31:08 -07002087void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2088 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002089 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002090 RETURN_ON_NULL(image);
2091 if (dst.isEmpty()) {
2092 return;
2093 }
msarett71df2d72016-09-30 12:41:42 -07002094
2095 SkIRect bounds;
2096 Lattice latticePlusBounds = lattice;
2097 if (!latticePlusBounds.fBounds) {
2098 bounds = SkIRect::MakeWH(image->width(), image->height());
2099 latticePlusBounds.fBounds = &bounds;
2100 }
2101
2102 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002103 LatticePaint latticePaint(paint);
2104 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002105 } else {
2106 this->drawImageRect(image, dst, paint);
2107 }
2108}
2109
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002110static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002111 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002112 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002113 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002114 return SkImage::MakeFromBitmap(bitmap);
2115}
2116
2117void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2118 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002119}
2120
reede47829b2015-08-06 10:02:53 -07002121void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002122 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002123 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002124}
2125
reed84984ef2015-07-17 07:09:43 -07002126void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2127 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002128 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002129}
2130
reede47829b2015-08-06 10:02:53 -07002131void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2132 SrcRectConstraint constraint) {
2133 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2134 constraint);
2135}
reede47829b2015-08-06 10:02:53 -07002136
reed71c3c762015-06-24 10:29:17 -07002137void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002138 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002139 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002140 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002141 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002142 if (count <= 0) {
2143 return;
2144 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002145 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002146 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002147 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002148}
2149
reedf70b5312016-03-04 16:36:20 -08002150void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002151 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002152 if (key) {
2153 this->onDrawAnnotation(rect, key, value);
2154 }
2155}
2156
reede47829b2015-08-06 10:02:53 -07002157void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2158 const SkPaint* paint, SrcRectConstraint constraint) {
2159 if (src) {
2160 this->drawImageRect(image, *src, dst, paint, constraint);
2161 } else {
2162 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2163 dst, paint, constraint);
2164 }
2165}
2166void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2167 const SkPaint* paint, SrcRectConstraint constraint) {
2168 if (src) {
2169 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2170 } else {
2171 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2172 dst, paint, constraint);
2173 }
2174}
2175
Mike Reed4204da22017-05-17 08:53:36 -04002176void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002177 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002178 this->onDrawShadowRec(path, rec);
2179}
2180
2181void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2182 SkPaint paint;
2183 const SkRect& pathBounds = path.getBounds();
2184
Mike Reed38992392019-07-30 10:48:15 -04002185 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002186 while (iter.next()) {
2187 iter.fDevice->drawShadow(path, rec);
2188 }
Mike Reed38992392019-07-30 10:48:15 -04002189 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002190}
2191
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002192void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002193 QuadAAFlags aaFlags, const SkColor4f& color,
2194 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002195 TRACE_EVENT0("skia", TRACE_FUNC);
2196 // Make sure the rect is sorted before passing it along
2197 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2198}
2199
2200void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2201 const SkPoint dstClips[],
2202 const SkMatrix preViewMatrices[],
2203 const SkPaint* paint,
2204 SrcRectConstraint constraint) {
2205 TRACE_EVENT0("skia", TRACE_FUNC);
2206 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2207}
2208
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209//////////////////////////////////////////////////////////////////////////////
2210// These are the virtual drawing methods
2211//////////////////////////////////////////////////////////////////////////////
2212
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002213void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002214 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002215 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2216 }
2217}
2218
reed41af9662015-01-05 07:49:08 -08002219void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002220 this->internalDrawPaint(paint);
2221}
2222
2223void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002224 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225
2226 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002227 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002228 }
2229
Mike Reed38992392019-07-30 10:48:15 -04002230 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231}
2232
reed41af9662015-01-05 07:49:08 -08002233void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2234 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 if ((long)count <= 0) {
2236 return;
2237 }
2238
Mike Reed822128b2017-02-28 16:41:03 -05002239 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002240 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002241 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002242 // special-case 2 points (common for drawing a single line)
2243 if (2 == count) {
2244 r.set(pts[0], pts[1]);
2245 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002246 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002247 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002248 if (!r.isFinite()) {
2249 return;
2250 }
Mike Reed822128b2017-02-28 16:41:03 -05002251 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002252 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2253 return;
2254 }
2255 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002256 }
reed@google.coma584aed2012-05-16 14:06:02 +00002257
halcanary96fcdcc2015-08-27 07:41:13 -07002258 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259
Mike Reed38992392019-07-30 10:48:15 -04002260 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002261
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002263 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 }
reed@google.com4b226022011-01-11 18:32:13 +00002265
Mike Reed38992392019-07-30 10:48:15 -04002266 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002267}
2268
reed4a167172016-08-18 17:15:25 -07002269static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002270 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002271}
2272
reed41af9662015-01-05 07:49:08 -08002273void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002274 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002276 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002277 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002278 return;
2279 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280 }
reed@google.com4b226022011-01-11 18:32:13 +00002281
reed4a167172016-08-18 17:15:25 -07002282 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002283 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284
reed4a167172016-08-18 17:15:25 -07002285 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002286 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002287 }
2288
Mike Reed38992392019-07-30 10:48:15 -04002289 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002290 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002291 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002292 SkDrawIter iter(this);
2293 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002294 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002295 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002296 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297}
2298
msarett44df6512016-08-25 13:54:30 -07002299void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002300 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002301 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002302 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002303 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2304 return;
2305 }
msarett44df6512016-08-25 13:54:30 -07002306 }
2307
Mike Reed38992392019-07-30 10:48:15 -04002308 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002309
2310 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002311 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002312 }
2313
Mike Reed38992392019-07-30 10:48:15 -04002314 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002315}
2316
Mike Reedd5674082019-04-19 15:00:47 -04002317void SkCanvas::onDrawBehind(const SkPaint& paint) {
2318 SkIRect bounds;
2319 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2320 for (;;) {
2321 const MCRec* rec = (const MCRec*)iter.prev();
2322 if (!rec) {
2323 return; // no backimages, so nothing to draw
2324 }
2325 if (rec->fBackImage) {
2326 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2327 rec->fBackImage->fImage->width(),
2328 rec->fBackImage->fImage->height());
2329 break;
2330 }
2331 }
2332
Mike Reed38992392019-07-30 10:48:15 -04002333 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002334
2335 while (iter.next()) {
2336 SkBaseDevice* dev = iter.fDevice;
2337
Mike Reedd5674082019-04-19 15:00:47 -04002338 dev->save();
2339 // We use clipRegion because it is already defined to operate in dev-space
2340 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2341 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002342 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002343 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002344 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002345 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002346 }
2347
Mike Reed38992392019-07-30 10:48:15 -04002348 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002349}
2350
reed41af9662015-01-05 07:49:08 -08002351void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002352 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002353 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002354 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002355 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002356 return;
2357 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002358 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002359
Mike Reed38992392019-07-30 10:48:15 -04002360 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002361
2362 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002363 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002364 }
2365
Mike Reed38992392019-07-30 10:48:15 -04002366 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002367}
2368
bsalomonac3aa242016-08-19 11:25:19 -07002369void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2370 SkScalar sweepAngle, bool useCenter,
2371 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002372 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002373 if (paint.canComputeFastBounds()) {
2374 SkRect storage;
2375 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002376 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002377 return;
2378 }
bsalomonac3aa242016-08-19 11:25:19 -07002379 }
2380
Mike Reed38992392019-07-30 10:48:15 -04002381 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002382
2383 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002384 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002385 }
2386
Mike Reed38992392019-07-30 10:48:15 -04002387 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002388}
2389
reed41af9662015-01-05 07:49:08 -08002390void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002391 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002392 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002393 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2394 return;
2395 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002396 }
2397
2398 if (rrect.isRect()) {
2399 // call the non-virtual version
2400 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002401 return;
2402 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002403 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002404 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2405 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002406 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002407
Mike Reed38992392019-07-30 10:48:15 -04002408 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002409
2410 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002411 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002412 }
2413
Mike Reed38992392019-07-30 10:48:15 -04002414 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002415}
2416
Mike Reed822128b2017-02-28 16:41:03 -05002417void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002418 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002419 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002420 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2421 return;
2422 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002423 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002424
Mike Reed38992392019-07-30 10:48:15 -04002425 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002426
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002427 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002428 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002429 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002430
Mike Reed38992392019-07-30 10:48:15 -04002431 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002432}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002433
reed41af9662015-01-05 07:49:08 -08002434void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002435 if (!path.isFinite()) {
2436 return;
2437 }
2438
Mike Reed822128b2017-02-28 16:41:03 -05002439 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002440 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002441 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002442 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2443 return;
2444 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002445 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002446
Mike Reed822128b2017-02-28 16:41:03 -05002447 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002448 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002449 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002450 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002451 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453
Mike Reed38992392019-07-30 10:48:15 -04002454 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002455
2456 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002457 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002458 }
2459
Mike Reed38992392019-07-30 10:48:15 -04002460 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002461}
2462
reed262a71b2015-12-05 13:07:27 -08002463bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002464 if (!paint.getImageFilter()) {
2465 return false;
2466 }
2467
2468 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002469 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002470 return false;
2471 }
2472
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002473 // The other paint effects need to be applied before the image filter, but the sprite draw
2474 // applies the filter explicitly first.
2475 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2476 return false;
2477 }
reed262a71b2015-12-05 13:07:27 -08002478 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2479 // Once we can filter and the filter will return a result larger than itself, we should be
2480 // able to remove this constraint.
2481 // skbug.com/4526
2482 //
2483 SkPoint pt;
2484 ctm.mapXY(x, y, &pt);
2485 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2486 return ir.contains(fMCRec->fRasterClip.getBounds());
2487}
2488
Mike Reedf441cfc2018-04-11 14:50:16 -04002489// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2490// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2491// null.
2492static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2493 if (paintParam) {
2494 *real = *paintParam;
2495 real->setStyle(SkPaint::kFill_Style);
2496 real->setPathEffect(nullptr);
2497 paintParam = real;
2498 }
2499 return paintParam;
2500}
2501
reeda85d4d02015-05-06 12:56:48 -07002502void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002503 SkPaint realPaint;
2504 paint = init_image_paint(&realPaint, paint);
2505
reeda85d4d02015-05-06 12:56:48 -07002506 SkRect bounds = SkRect::MakeXYWH(x, y,
2507 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002508 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002509 SkRect tmp = bounds;
2510 if (paint) {
2511 paint->computeFastBounds(tmp, &tmp);
2512 }
2513 if (this->quickReject(tmp)) {
2514 return;
2515 }
reeda85d4d02015-05-06 12:56:48 -07002516 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002517 // At this point we need a real paint object. If the caller passed null, then we should
2518 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2519 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2520 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002521
reeda2217ef2016-07-20 06:04:34 -07002522 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002523 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2524 *paint);
2525 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002526 special = this->getDevice()->makeSpecial(image);
2527 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002528 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002529 }
2530 }
2531
Mike Reed38992392019-07-30 10:48:15 -04002532 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002533
reeda85d4d02015-05-06 12:56:48 -07002534 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002535 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002536 if (special) {
2537 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002538 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002539 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002540 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002541 SkScalarRoundToInt(pt.fY), pnt,
2542 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002543 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002544 iter.fDevice->drawImageRect(
2545 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2546 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002547 }
reeda85d4d02015-05-06 12:56:48 -07002548 }
halcanary9d524f22016-03-29 09:03:52 -07002549
Mike Reed38992392019-07-30 10:48:15 -04002550 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002551}
2552
reed41af9662015-01-05 07:49:08 -08002553void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002554 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002555 SkPaint realPaint;
2556 paint = init_image_paint(&realPaint, paint);
2557
halcanary96fcdcc2015-08-27 07:41:13 -07002558 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002559 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002560 if (paint) {
2561 paint->computeFastBounds(dst, &storage);
2562 }
2563 if (this->quickReject(storage)) {
2564 return;
2565 }
reeda85d4d02015-05-06 12:56:48 -07002566 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002567 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002568
Mike Reed38992392019-07-30 10:48:15 -04002569 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002570
reeda85d4d02015-05-06 12:56:48 -07002571 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002572 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002573 }
halcanary9d524f22016-03-29 09:03:52 -07002574
Mike Reed38992392019-07-30 10:48:15 -04002575 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002576}
2577
reed4c21dc52015-06-25 12:32:03 -07002578void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2579 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002580 SkPaint realPaint;
2581 paint = init_image_paint(&realPaint, paint);
2582
halcanary96fcdcc2015-08-27 07:41:13 -07002583 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002584 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002585 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2586 return;
2587 }
reed@google.com3d608122011-11-21 15:16:16 +00002588 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002589 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002590
Mike Reed38992392019-07-30 10:48:15 -04002591 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002592
reed4c21dc52015-06-25 12:32:03 -07002593 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002594 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002595 }
halcanary9d524f22016-03-29 09:03:52 -07002596
Mike Reed38992392019-07-30 10:48:15 -04002597 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002598}
2599
msarett16882062016-08-16 09:31:08 -07002600void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2601 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002602 SkPaint realPaint;
2603 paint = init_image_paint(&realPaint, paint);
2604
msarett16882062016-08-16 09:31:08 -07002605 if (nullptr == paint || paint->canComputeFastBounds()) {
2606 SkRect storage;
2607 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2608 return;
2609 }
2610 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002611 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002612
Mike Reed38992392019-07-30 10:48:15 -04002613 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002614
2615 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002616 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002617 }
2618
Mike Reed38992392019-07-30 10:48:15 -04002619 DRAW_END
msarett16882062016-08-16 09:31:08 -07002620}
2621
fmalita00d5c2c2014-08-21 08:53:26 -07002622void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2623 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002624 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002625 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002626 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002627 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002628 SkRect tmp;
2629 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2630 return;
2631 }
2632 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002633 }
2634
fmalita024f9962015-03-03 19:08:17 -08002635 // We cannot filter in the looper as we normally do, because the paint is
2636 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002637 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002638
fmalitaaa1b9122014-08-28 14:32:24 -07002639 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002640 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002641 }
2642
Mike Reed38992392019-07-30 10:48:15 -04002643 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002644}
2645
Mike Reed358fcad2018-11-23 15:27:51 -05002646// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002647void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002648 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2649 TRACE_EVENT0("skia", TRACE_FUNC);
2650 if (byteLength) {
2651 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002652 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002653 }
2654}
Mike Reed4f81bb72019-01-23 09:23:00 -05002655
fmalita00d5c2c2014-08-21 08:53:26 -07002656void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2657 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002658 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002659 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002660 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002661 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002662}
reed@google.come0d9ce82014-04-23 04:00:17 +00002663
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002664#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002665void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002666 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002667 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002668
2669 while (iter.next()) {
2670 // In the common case of one iteration we could std::move vertices here.
Mike Reed5caf9352020-03-02 14:57:09 -05002671 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002672 }
2673
Mike Reed38992392019-07-30 10:48:15 -04002674 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002675}
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002676#else
2677void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2678 const SkPaint& paint) {
2679 DRAW_BEGIN(paint, nullptr)
2680
2681 while (iter.next()) {
2682 // In the common case of one iteration we could std::move vertices here.
2683 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2684 }
2685
2686 DRAW_END
2687}
2688#endif
Brian Salomon199fb872017-02-06 09:41:10 -05002689
dandovb3c9d1c2014-08-12 08:34:29 -07002690void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002691 const SkPoint texCoords[4], SkBlendMode bmode,
2692 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002693 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002694 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002695 return;
2696 }
mtklein6cfa73a2014-08-13 13:33:49 -07002697
Mike Reedfaba3712016-11-03 14:45:31 -04002698 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002699}
2700
2701void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002702 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002703 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002704 // Since a patch is always within the convex hull of the control points, we discard it when its
2705 // bounding rectangle is completely outside the current clip.
2706 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002707 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002708 if (this->quickReject(bounds)) {
2709 return;
2710 }
mtklein6cfa73a2014-08-13 13:33:49 -07002711
Mike Reed38992392019-07-30 10:48:15 -04002712 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002713
dandovecfff212014-08-04 10:02:00 -07002714 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002715 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002716 }
mtklein6cfa73a2014-08-13 13:33:49 -07002717
Mike Reed38992392019-07-30 10:48:15 -04002718 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002719}
2720
reeda8db7282015-07-07 10:22:31 -07002721void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002722#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002723 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002724#endif
reede3b38ce2016-01-08 09:18:44 -08002725 RETURN_ON_NULL(dr);
2726 if (x || y) {
2727 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2728 this->onDrawDrawable(dr, &matrix);
2729 } else {
2730 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002731 }
2732}
2733
reeda8db7282015-07-07 10:22:31 -07002734void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002735#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002736 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002737#endif
reede3b38ce2016-01-08 09:18:44 -08002738 RETURN_ON_NULL(dr);
2739 if (matrix && matrix->isIdentity()) {
2740 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002741 }
reede3b38ce2016-01-08 09:18:44 -08002742 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002743}
2744
2745void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002746 // drawable bounds are no longer reliable (e.g. android displaylist)
2747 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002748 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002749}
2750
reed71c3c762015-06-24 10:29:17 -07002751void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002752 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002753 const SkRect* cull, const SkPaint* paint) {
2754 if (cull && this->quickReject(*cull)) {
2755 return;
2756 }
2757
2758 SkPaint pnt;
2759 if (paint) {
2760 pnt = *paint;
2761 }
halcanary9d524f22016-03-29 09:03:52 -07002762
Mike Reed38992392019-07-30 10:48:15 -04002763 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002764 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002765 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002766 }
Mike Reed38992392019-07-30 10:48:15 -04002767 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002768}
2769
reedf70b5312016-03-04 16:36:20 -08002770void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2771 SkASSERT(key);
2772
2773 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002774 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002775 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002776 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002777 }
Mike Reed38992392019-07-30 10:48:15 -04002778 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002779}
2780
Michael Ludwiga595f862019-08-27 15:25:49 -04002781void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2782 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002783 SkASSERT(r.isSorted());
2784
2785 // If this used a paint, it would be a filled color with blend mode, which does not
2786 // need to use an autodraw loop, so use SkDrawIter directly.
2787 if (this->quickReject(r)) {
2788 return;
2789 }
2790
Michael Ludwiga4b44882019-08-28 14:34:58 -04002791 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002792 SkDrawIter iter(this);
2793 while(iter.next()) {
2794 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2795 }
2796}
2797
2798void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2799 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2800 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002801 if (count <= 0) {
2802 // Nothing to draw
2803 return;
2804 }
2805
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002806 SkPaint realPaint;
2807 init_image_paint(&realPaint, paint);
2808
Michael Ludwiga4b44882019-08-28 14:34:58 -04002809 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2810 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2811 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2812 // or we need it for the autolooper (since it greatly improves image filter perf).
2813 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2814 bool setBoundsValid = count == 1 || needsAutoLooper;
2815 SkRect setBounds = imageSet[0].fDstRect;
2816 if (imageSet[0].fMatrixIndex >= 0) {
2817 // Account for the per-entry transform that is applied prior to the CTM when drawing
2818 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002819 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002820 if (needsAutoLooper) {
2821 for (int i = 1; i < count; ++i) {
2822 SkRect entryBounds = imageSet[i].fDstRect;
2823 if (imageSet[i].fMatrixIndex >= 0) {
2824 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2825 }
2826 setBounds.joinPossiblyEmptyRect(entryBounds);
2827 }
2828 }
2829
2830 // If we happen to have the draw bounds, though, might as well check quickReject().
2831 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2832 SkRect tmp;
2833 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2834 return;
2835 }
2836 }
2837
2838 if (needsAutoLooper) {
2839 SkASSERT(setBoundsValid);
2840 DRAW_BEGIN(realPaint, &setBounds)
2841 while (iter.next()) {
2842 iter.fDevice->drawEdgeAAImageSet(
2843 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2844 }
2845 DRAW_END
2846 } else {
2847 this->predrawNotify();
2848 SkDrawIter iter(this);
2849 while(iter.next()) {
2850 iter.fDevice->drawEdgeAAImageSet(
2851 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2852 }
2853 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002854}
2855
reed@android.com8a1c16f2008-12-17 15:59:43 +00002856//////////////////////////////////////////////////////////////////////////////
2857// These methods are NOT virtual, and therefore must call back into virtual
2858// methods, rather than actually drawing themselves.
2859//////////////////////////////////////////////////////////////////////////////
2860
reed374772b2016-10-05 17:33:02 -07002861void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002863 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002864 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 this->drawPaint(paint);
2866}
2867
2868void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002869 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002870 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2871}
2872
Mike Reed3661bc92017-02-22 13:21:42 -05002873void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002875 pts[0].set(x0, y0);
2876 pts[1].set(x1, y1);
2877 this->drawPoints(kLines_PointMode, 2, pts, paint);
2878}
2879
Mike Reed3661bc92017-02-22 13:21:42 -05002880void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881 if (radius < 0) {
2882 radius = 0;
2883 }
2884
2885 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002886 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002887 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888}
2889
2890void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2891 const SkPaint& paint) {
2892 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002893 SkRRect rrect;
2894 rrect.setRectXY(r, rx, ry);
2895 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002896 } else {
2897 this->drawRect(r, paint);
2898 }
2899}
2900
reed@android.com8a1c16f2008-12-17 15:59:43 +00002901void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2902 SkScalar sweepAngle, bool useCenter,
2903 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002904 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002905 if (oval.isEmpty() || !sweepAngle) {
2906 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002907 }
bsalomon21af9ca2016-08-25 12:29:23 -07002908 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002909}
2910
reed@android.comf76bacf2009-05-13 14:00:33 +00002911///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002912#ifdef SK_DISABLE_SKPICTURE
2913void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002914
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002915
2916void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2917 const SkPaint* paint) {}
2918#else
Mike Klein88d90712018-01-27 17:30:04 +00002919/**
2920 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2921 * against the playback cost of recursing into the subpicture to get at its actual ops.
2922 *
2923 * For now we pick a conservatively small value, though measurement (and other heuristics like
2924 * the type of ops contained) may justify changing this value.
2925 */
2926#define kMaxPictureOpsToUnrollInsteadOfRef 1
2927
reedd5fa1a42014-08-09 11:08:05 -07002928void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002929 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002930 RETURN_ON_NULL(picture);
2931
reede3b38ce2016-01-08 09:18:44 -08002932 if (matrix && matrix->isIdentity()) {
2933 matrix = nullptr;
2934 }
Mike Klein88d90712018-01-27 17:30:04 +00002935 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2936 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2937 picture->playback(this);
2938 } else {
2939 this->onDrawPicture(picture, matrix, paint);
2940 }
reedd5fa1a42014-08-09 11:08:05 -07002941}
robertphillips9b14f262014-06-04 05:40:44 -07002942
reedd5fa1a42014-08-09 11:08:05 -07002943void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2944 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002945 if (!paint || paint->canComputeFastBounds()) {
2946 SkRect bounds = picture->cullRect();
2947 if (paint) {
2948 paint->computeFastBounds(bounds, &bounds);
2949 }
2950 if (matrix) {
2951 matrix->mapRect(&bounds);
2952 }
2953 if (this->quickReject(bounds)) {
2954 return;
2955 }
2956 }
2957
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002958 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002959 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002961#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962
reed@android.com8a1c16f2008-12-17 15:59:43 +00002963///////////////////////////////////////////////////////////////////////////////
2964///////////////////////////////////////////////////////////////////////////////
2965
reed3aafe112016-08-18 12:45:34 -07002966SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002967 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002968
2969 SkASSERT(canvas);
2970
reed3aafe112016-08-18 12:45:34 -07002971 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002972 fDone = !fImpl->next();
2973}
2974
2975SkCanvas::LayerIter::~LayerIter() {
2976 fImpl->~SkDrawIter();
2977}
2978
2979void SkCanvas::LayerIter::next() {
2980 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002981 if (!fDone) {
2982 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2983 // filters, so its devices will always be able to report the origin exactly.
2984 fDeviceOrigin = fImpl->fDevice->getOrigin();
2985 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986}
2987
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002988SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002989 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002990}
2991
2992const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002993 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002994}
2995
2996const SkPaint& SkCanvas::LayerIter::paint() const {
2997 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002998 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002999 paint = &fDefaultPaint;
3000 }
3001 return *paint;
3002}
3003
Mike Reedca37f322018-03-08 13:22:16 -05003004SkIRect SkCanvas::LayerIter::clipBounds() const {
3005 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003006}
3007
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003008int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
3009int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003010
3011///////////////////////////////////////////////////////////////////////////////
3012
Brian Osmane8a98632019-04-10 10:26:10 -04003013SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3014SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3015SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3016SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3017
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003018SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3019 const SkRect& dstRect, int matrixIndex, float alpha,
3020 unsigned aaFlags, bool hasClip)
3021 : fImage(std::move(image))
3022 , fSrcRect(srcRect)
3023 , fDstRect(dstRect)
3024 , fMatrixIndex(matrixIndex)
3025 , fAlpha(alpha)
3026 , fAAFlags(aaFlags)
3027 , fHasClip(hasClip) {}
3028
3029SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3030 const SkRect& dstRect, float alpha, unsigned aaFlags)
3031 : fImage(std::move(image))
3032 , fSrcRect(srcRect)
3033 , fDstRect(dstRect)
3034 , fAlpha(alpha)
3035 , fAAFlags(aaFlags) {}
3036
3037///////////////////////////////////////////////////////////////////////////////
3038
Mike Reed5df49342016-11-12 08:06:55 -06003039std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003040 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003041 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003042 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003043 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003044
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003045 SkBitmap bitmap;
3046 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003047 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003048 }
Mike Reed12f77342017-11-08 11:19:52 -05003049
3050 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003051 std::make_unique<SkCanvas>(bitmap, *props) :
3052 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003053}
reedd5fa1a42014-08-09 11:08:05 -07003054
3055///////////////////////////////////////////////////////////////////////////////
3056
Florin Malitaee424ac2016-12-01 12:47:59 -05003057SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003058 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003059
Florin Malita439ace92016-12-02 12:05:41 -05003060SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003061 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003062
Herb Derbyefe39bc2018-05-01 17:06:20 -04003063SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003064 : INHERITED(device) {}
3065
Florin Malitaee424ac2016-12-01 12:47:59 -05003066SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3067 (void)this->INHERITED::getSaveLayerStrategy(rec);
3068 return kNoLayer_SaveLayerStrategy;
3069}
3070
Mike Reed148b7fd2018-12-18 17:38:18 -05003071bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3072 return false;
3073}
3074
Florin Malitaee424ac2016-12-01 12:47:59 -05003075///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003076
reed73603f32016-09-20 08:42:38 -07003077static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3078static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3079static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3080static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3081static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3082static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003083
3084///////////////////////////////////////////////////////////////////////////////////////////////////
3085
3086SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3087 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003088 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003089 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003090 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003091 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003092 clip.setEmpty();
3093 }
3094
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003095 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003096 return handle;
3097 }
3098 return nullptr;
3099}
3100
3101static bool install(SkBitmap* bm, const SkImageInfo& info,
3102 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003103 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003104}
3105
3106SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3107 SkBitmap* bm) {
3108 SkRasterHandleAllocator::Rec rec;
3109 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3110 return nullptr;
3111 }
3112 return rec.fHandle;
3113}
3114
3115std::unique_ptr<SkCanvas>
3116SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3117 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003118 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003119 return nullptr;
3120 }
3121
3122 SkBitmap bm;
3123 Handle hndl;
3124
3125 if (rec) {
3126 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3127 } else {
3128 hndl = alloc->allocBitmap(info, &bm);
3129 }
3130 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3131}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003132
3133///////////////////////////////////////////////////////////////////////////////////////////////////