blob: 3b6abc00b0ac05bdd4488d9010cdbee1d6708e03 [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"
26#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Reedeb1d5a22020-04-14 09:16:40 -040035#include "src/core/SkMarkerStack.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;
Brian Osmand8f611d2020-04-21 15:43:47 -0400242 SkM44 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;
Brian Osmand8f611d2020-04-21 15:43:47 -0400248 fMatrix.setIdentity();
reed2ff1fce2014-12-11 07:07:37 -0800249 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
Brian Osmand8f611d2020-04-21 15:43:47 -0400271 fMatrix.setIdentity();
mtkleinfeaadee2015-04-08 11:25:48 -0700272 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) {
Mike Reedeb1d5a22020-04-14 09:16:40 -0400498 fMarkerStack = sk_make_sp<SkMarkerStack>();
499
reed2ff1fce2014-12-11 07:07:37 -0800500 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501
502 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500503 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500504 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700505 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506
reeda499f902015-05-01 09:34:31 -0700507 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
508 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Brian Osmand8f611d2020-04-21 15:43:47 -0400509 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix.asM33(), nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512
halcanary96fcdcc2015-08-27 07:41:13 -0700513 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000514
reedf92c8662014-08-18 08:02:43 -0700515 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700516 // The root device and the canvas should always have the same pixel geometry
517 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800518 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700519 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500520
Mike Reedc42a1cd2017-02-14 14:25:14 -0500521 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
Mike Reedeb1d5a22020-04-14 09:16:40 -0400522
523 device->setMarkerStack(fMarkerStack.get());
reedf92c8662014-08-18 08:02:43 -0700524 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400525
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500526 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527}
528
reed@google.comcde92112011-07-06 20:00:52 +0000529SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000530 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700531 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000532{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000533 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000534
Hal Canary363a3f82018-10-04 11:04:48 -0400535 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000536}
537
reed96a857e2015-01-25 10:33:58 -0800538SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000539 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800540 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000541{
542 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400543 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500544 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700545}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000546
Hal Canary363a3f82018-10-04 11:04:48 -0400547SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700548 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700549 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700550{
551 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700552
Mike Reed566e53c2017-03-10 10:49:45 -0500553 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400554 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700555}
556
Herb Derbyefe39bc2018-05-01 17:06:20 -0400557SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000558 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700559 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000560{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700562
Hal Canary363a3f82018-10-04 11:04:48 -0400563 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700564}
565
reed4a8126e2014-09-22 07:29:03 -0700566SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700567 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700568 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700569{
570 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700571
Mike Reed910ca0f2018-04-25 13:04:05 -0400572 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400573 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700574}
reed29c857d2014-09-21 10:25:07 -0700575
Mike Reed356f7c22017-01-10 11:58:39 -0500576SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
577 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700578 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
579 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500580 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700581{
582 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700583
Mike Reed910ca0f2018-04-25 13:04:05 -0400584 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400585 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586}
587
Mike Reed356f7c22017-01-10 11:58:39 -0500588SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
589
Matt Sarett31f99ce2017-04-11 08:46:01 -0400590#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
591SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
592 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
593 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
594 , fAllocator(nullptr)
595{
596 inc_canvas();
597
598 SkBitmap tmp(bitmap);
599 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400600 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400601 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400602}
603#endif
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605SkCanvas::~SkCanvas() {
606 // free up the contents of our deque
607 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 this->internalRestore(); // restore the last, since we're going away
610
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 dec_canvas();
612}
613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614///////////////////////////////////////////////////////////////////////////////
615
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000616void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700617 this->onFlush();
618}
619
620void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000621 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000622 if (device) {
623 device->flush();
624 }
625}
626
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500627SkSurface* SkCanvas::getSurface() const {
628 return fSurfaceBase;
629}
630
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000631SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000632 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000633 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
634}
635
senorblancoafc7cce2016-02-02 18:44:15 -0800636SkIRect SkCanvas::getTopLayerBounds() const {
637 SkBaseDevice* d = this->getTopDevice();
638 if (!d) {
639 return SkIRect::MakeEmpty();
640 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500641 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800642}
643
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000644SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000646 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400648 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649}
650
Florin Malita0ed3b642017-01-13 16:56:38 +0000651SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400652 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000653}
654
Mike Reed353196f2017-07-21 11:01:18 -0400655bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000656 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400657 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000658}
659
Mike Reed353196f2017-07-21 11:01:18 -0400660bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
661 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400662}
663
664bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
665 SkPixmap pm;
666 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
667}
668
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000669bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400670 SkPixmap pm;
671 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700672 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 }
674 return false;
675}
676
Matt Sarett03dd6d52017-01-23 12:15:09 -0500677bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000678 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000679 SkBaseDevice* device = this->getDevice();
680 if (!device) {
681 return false;
682 }
683
Matt Sarett03dd6d52017-01-23 12:15:09 -0500684 // This check gives us an early out and prevents generation ID churn on the surface.
685 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
686 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400687 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500688 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000689 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000690
Matt Sarett03dd6d52017-01-23 12:15:09 -0500691 // Tell our owning surface to bump its generation ID.
692 const bool completeOverwrite =
693 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700694 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700695
Matt Sarett03dd6d52017-01-23 12:15:09 -0500696 // This can still fail, most notably in the case of a invalid color type or alpha type
697 // conversion. We could pull those checks into this function and avoid the unnecessary
698 // generation ID bump. But then we would be performing those checks twice, since they
699 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400700 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000701}
reed@google.com51df9e32010-12-23 19:29:18 +0000702
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703//////////////////////////////////////////////////////////////////////////////
704
reed2ff1fce2014-12-11 07:07:37 -0800705void SkCanvas::checkForDeferredSave() {
706 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800707 this->doSave();
708 }
709}
710
reedf0090cb2014-11-26 08:55:51 -0800711int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800712#ifdef SK_DEBUG
713 int count = 0;
714 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
715 for (;;) {
716 const MCRec* rec = (const MCRec*)iter.next();
717 if (!rec) {
718 break;
719 }
720 count += 1 + rec->fDeferredSaveCount;
721 }
722 SkASSERT(count == fSaveCount);
723#endif
724 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800725}
726
727int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800728 fSaveCount += 1;
729 fMCRec->fDeferredSaveCount += 1;
730 return this->getSaveCount() - 1; // return our prev value
731}
732
733void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800734 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700735
736 SkASSERT(fMCRec->fDeferredSaveCount > 0);
737 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800738 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800739}
740
741void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800742 if (fMCRec->fDeferredSaveCount > 0) {
743 SkASSERT(fSaveCount > 1);
744 fSaveCount -= 1;
745 fMCRec->fDeferredSaveCount -= 1;
746 } else {
747 // check for underflow
748 if (fMCStack.count() > 1) {
749 this->willRestore();
750 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700751 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800752 this->internalRestore();
753 this->didRestore();
754 }
reedf0090cb2014-11-26 08:55:51 -0800755 }
756}
757
758void SkCanvas::restoreToCount(int count) {
759 // sanity check
760 if (count < 1) {
761 count = 1;
762 }
mtkleinf0f14112014-12-12 08:46:25 -0800763
reedf0090cb2014-11-26 08:55:51 -0800764 int n = this->getSaveCount() - count;
765 for (int i = 0; i < n; ++i) {
766 this->restore();
767 }
768}
769
reed2ff1fce2014-12-11 07:07:37 -0800770void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700772 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000774
Mike Reedc42a1cd2017-02-14 14:25:14 -0500775 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776}
777
reed4960eee2015-12-18 07:09:18 -0800778bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400779 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780}
781
reed4960eee2015-12-18 07:09:18 -0800782bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700783 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400784 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
785 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
786 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
787 // filter does not require a decomposed CTM matrix, the filter space and device space are the
788 // same. When it has been decomposed, we want the original image filter node to process the
789 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
790 // longer the original filter, but has the remainder matrix baked into it, and passing in the
791 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
792 // to the original filter node (barring inflation from consecutive calls to mapRect). While
793 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
794 // passing getDeviceClipBounds() to 'imageFilter' is correct.
795 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
796 // be important to more accurately calculate the clip bounds in the layer space for the original
797 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500798 SkIRect clipBounds = this->getDeviceClipBounds();
799 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000800 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000801 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000802
Brian Osmand8f611d2020-04-21 15:43:47 -0400803 const SkMatrix& ctm = fMCRec->fMatrix.asM33(); // this->getTotalMatrix()
reed96e657d2015-03-10 17:30:07 -0700804
Robert Phillips12078432018-05-17 11:17:39 -0400805 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
806 // If the image filter DAG affects transparent black then we will need to render
807 // out to the clip bounds
808 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000809 }
Robert Phillips12078432018-05-17 11:17:39 -0400810
811 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700812 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700814 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400815 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400817 inputSaveLayerBounds = clipBounds;
818 }
819
820 if (imageFilter) {
821 // expand the clip bounds by the image filter DAG to include extra content that might
822 // be required by the image filters.
823 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
824 SkImageFilter::kReverse_MapDirection,
825 &inputSaveLayerBounds);
826 }
827
828 SkIRect clippedSaveLayerBounds;
829 if (bounds) {
830 // For better or for worse, user bounds currently act as a hard clip on the layer's
831 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
832 clippedSaveLayerBounds = inputSaveLayerBounds;
833 } else {
834 // If there are no user bounds, we don't want to artificially restrict the resulting
835 // layer bounds, so allow the expanded clip bounds free reign.
836 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800838
839 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400840 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800841 if (BoundsAffectsClip(saveLayerFlags)) {
842 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
843 fMCRec->fRasterClip.setEmpty();
844 fDeviceClipBounds.setEmpty();
845 }
846 return false;
847 }
Robert Phillips12078432018-05-17 11:17:39 -0400848 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849
reed4960eee2015-12-18 07:09:18 -0800850 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700851 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400852 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
853 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000854 }
855
856 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400857 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000858 }
Robert Phillips12078432018-05-17 11:17:39 -0400859
junov@chromium.orga907ac32012-02-24 21:54:07 +0000860 return true;
861}
862
reed4960eee2015-12-18 07:09:18 -0800863int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
864 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000865}
866
Cary Clarke041e312018-03-06 13:00:52 -0500867int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700868 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400869 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
870 // no need for the layer (or any of the draws until the matching restore()
871 this->save();
872 this->clipRect({0,0,0,0});
873 } else {
874 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
875 fSaveCount += 1;
876 this->internalSaveLayer(rec, strategy);
877 }
reed4960eee2015-12-18 07:09:18 -0800878 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800879}
880
Mike Reed148b7fd2018-12-18 17:38:18 -0500881int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
882 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
883 // Assuming clips never expand, if the request bounds is outside of the current clip
884 // there is no need to copy/restore the area, so just devolve back to a regular save.
885 this->save();
886 } else {
887 bool doTheWork = this->onDoSaveBehind(bounds);
888 fSaveCount += 1;
889 this->internalSave();
890 if (doTheWork) {
891 this->internalSaveBehind(bounds);
892 }
893 }
894 return this->getSaveCount() - 1;
895}
896
reeda2217ef2016-07-20 06:04:34 -0700897void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500898 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500899 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400900 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
901 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400902 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500903 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
904 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400905 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400906 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
907 // This means that we only have to copy a dst-sized block of pixels out of src and translate
908 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400909 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
910 dstOrigin.y() - src->getOrigin().y(),
911 dst->width(), dst->height());
912 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400913 return;
914 }
915
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400916 auto special = src->snapSpecial(snapBounds);
917 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400918 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
919 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400920 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
921 }
922 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400923 }
924
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400925 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
926 // by the backdrop filter.
927 SkMatrix toRoot, layerMatrix;
928 SkSize scale;
929 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
930 toRoot = SkMatrix::I();
931 layerMatrix = ctm;
932 } else if (ctm.decomposeScale(&scale, &toRoot)) {
933 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
934 } else {
935 // Perspective, for now, do no scaling of the layer itself.
936 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
937 // the matrix, e.g. based on the midpoint of the near/far planes?
938 toRoot = ctm;
939 layerMatrix = SkMatrix::I();
940 }
941
942 // We have to map the dst bounds from the root space into the layer space where filtering will
943 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
944 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
945 // is a safe, conservative estimate.
946 SkMatrix fromRoot;
947 if (!toRoot.invert(&fromRoot)) {
948 return;
949 }
950
951 // This represents what the backdrop filter needs to produce in the layer space, and is sized
952 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
953 SkIRect layerTargetBounds = fromRoot.mapRect(
954 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
955 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
956 // require some extra input pixels.
957 SkIRect layerInputBounds = filter->filterBounds(
958 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
959 &layerTargetBounds);
960
961 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400962 // be the conservative contents required to fill a layerInputBounds-sized surface with the
963 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400964 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
965 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
966 if (!backdropBounds.intersect(srcDevRect)) {
967 return;
968 }
969
970 auto special = src->snapSpecial(backdropBounds);
971 if (!special) {
972 return;
973 }
974
975 SkColorType colorType = src->imageInfo().colorType();
976 if (colorType == kUnknown_SkColorType) {
977 colorType = kRGBA_8888_SkColorType;
978 }
979 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400980
981 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400982 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400983 // Drawing the temporary and final filtered image requires a higher filter quality if the
984 // 'toRoot' transformation is not identity, in order to minimize the impact on already
985 // rendered edges/content.
986 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
987 p.setFilterQuality(kHigh_SkFilterQuality);
988
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400989 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
990 // and stored in a temporary surface, which is then used as the input to the actual filter.
991 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
992 if (!tmpSurface) {
993 return;
994 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400995
996 auto tmpCanvas = tmpSurface->getCanvas();
997 tmpCanvas->clear(SK_ColorTRANSPARENT);
998 // Reading in reverse, this takes the backdrop bounds from src device space into the root
999 // space, then maps from root space into the layer space, then maps it so the input layer's
1000 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1001 // performed on backdropBounds.
1002 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1003 tmpCanvas->concat(fromRoot);
1004 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001005
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001006 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1007 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1008 special = tmpSurface->makeImageSnapshot();
1009 } else {
1010 // Since there is no extra transform that was done, update the input bounds to reflect
1011 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1012 // was equal to backdropBounds before it was made relative to the src device and cropped.
1013 // When we use the original snapped image directly, just map the update backdrop bounds
1014 // back into the shared layer space
1015 layerInputBounds = backdropBounds;
1016 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001017
1018 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1019 // draw will be 1-1 so there is no need to increase filter quality.
1020 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001021 }
1022
1023 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1024 // layer space. This has to further offset everything so that filter evaluation thinks the
1025 // source image's top left corner is (0, 0).
1026 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1027 // this can be simplified.
1028 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1029 SkMatrix filterCTM = layerMatrix;
1030 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1031 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1032
1033 SkIPoint offset;
1034 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001035 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001036 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1037 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1038 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1039 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001040 offset += layerInputBounds.topLeft();
1041
1042 // Manually setting the device's CTM requires accounting for the device's origin.
1043 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001044 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001045 // a global CTM instead of a device CTM.
1046 SkMatrix dstCTM = toRoot;
1047 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001048 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001049
1050 // And because devices don't have a special-image draw function that supports arbitrary
1051 // matrices, we are abusing the asImage() functionality here...
1052 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001053 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001054 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001055 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001056 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1057 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001058 }
robertphillips7354a4b2015-12-16 05:08:27 -08001059}
reed70ee31b2015-12-10 13:44:45 -08001060
Mike Kleine083f7c2018-02-07 12:54:27 -05001061static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001062 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001063 if (prev.bytesPerPixel() <= 4 &&
1064 prev.colorType() != kRGBA_8888_SkColorType &&
1065 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001066 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1067 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1068 ct = kN32_SkColorType;
1069 }
1070 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001071}
1072
reed4960eee2015-12-18 07:09:18 -08001073void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001074 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001075 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001076 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1077
Michael Ludwigeced98b2020-03-03 10:39:41 -05001078 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1079 // saveLayer ignores mask filters, so force it to null
1080 if (paint.get() && paint->getMaskFilter()) {
1081 paint.writable()->setMaskFilter(nullptr);
1082 }
1083
Mike Reed5532c2a2019-02-23 12:00:32 -05001084 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1085 // regardless of any hint-rect from the caller. skbug.com/8783
1086 if (rec.fBackdrop) {
1087 bounds = nullptr;
1088 }
1089
Michael Ludwigeced98b2020-03-03 10:39:41 -05001090 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
Brian Osmand8f611d2020-04-21 15:43:47 -04001091 SkMatrix stashedMatrix = fMCRec->fMatrix.asM33();
Robert Phillips3d0e8502018-04-20 10:27:27 -04001092 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001093
reed8c30a812016-04-20 16:36:51 -07001094 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001095 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1096 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1097 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001098 *
1099 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001100 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1101 * if necessary.
1102 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1103 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001104 * 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 -04001105 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001106 * of the original imagefilter, and draw that (via drawSprite)
1107 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1108 *
1109 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1110 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1111 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001112 if (imageFilter) {
1113 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001114 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1115 &modifiedCTM);
1116 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001117 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001118 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001119 modifiedRec = fMCRec;
1120 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001121 imageFilter = modifiedFilter.get();
1122 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001123 }
1124 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1125 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001126 }
reed8c30a812016-04-20 16:36:51 -07001127
junov@chromium.orga907ac32012-02-24 21:54:07 +00001128 // do this before we create the layer. We don't call the public save() since
1129 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001130 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001131
junov@chromium.orga907ac32012-02-24 21:54:07 +00001132 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001133 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001134 if (modifiedRec) {
1135 // In this case there will be no layer in which to stash the matrix so we need to
1136 // revert the prior MCRec to its earlier state.
Brian Osmand8f611d2020-04-21 15:43:47 -04001137 modifiedRec->fMatrix = SkM44(stashedMatrix);
Robert Phillips3d0e8502018-04-20 10:27:27 -04001138 }
reed2ff1fce2014-12-11 07:07:37 -08001139 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 }
1141
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001142 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1143 // the clipRectBounds() call above?
1144 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001145 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001146 }
1147
reed8dc0ccb2015-03-20 06:32:52 -07001148 SkPixelGeometry geo = fProps.pixelGeometry();
1149 if (paint) {
reed76033be2015-03-14 10:54:31 -07001150 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001151 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001152 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001153 }
1154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155
robertphillips5139e502016-07-19 05:10:40 -07001156 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001157 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001158 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001159 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001160 }
reedb2db8982014-11-13 12:41:02 -08001161
Mike Kleine083f7c2018-02-07 12:54:27 -05001162 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001163 if (rec.fSaveLayerFlags & kF16ColorType) {
1164 info = info.makeColorType(kRGBA_F16_SkColorType);
1165 }
reed129ed1c2016-02-22 06:42:31 -08001166
Hal Canary704cd322016-11-07 14:13:52 -05001167 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001168 {
Florin Malita4571e492019-07-16 10:25:58 -04001169 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001170 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001171 const bool trackCoverage =
1172 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001173 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001174 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001175 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001176 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1177 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001178 return;
reed61f501f2015-04-29 08:34:00 -07001179 }
Mike Reedeb1d5a22020-04-14 09:16:40 -04001180 newDevice->setMarkerStack(fMarkerStack.get());
bungeman@google.come25c6842011-08-17 14:53:54 +00001181 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001182 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183
Mike Reedb43a3e02017-02-11 10:18:58 -05001184 // only have a "next" if this new layer doesn't affect the clip (rare)
1185 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 fMCRec->fLayer = layer;
1187 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001188
Mike Reedc61abee2017-02-28 17:45:27 -05001189 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001190 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Brian Osmand8f611d2020-04-21 15:43:47 -04001191 fMCRec->fMatrix.asM33());
reeda2217ef2016-07-20 06:04:34 -07001192 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001193
Brian Osmanf8865052020-04-22 15:44:55 -04001194 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
Mike Reedc42a1cd2017-02-14 14:25:14 -05001195
1196 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1197 if (layer->fNext) {
1198 // need to punch a hole in the previous device, so we don't draw there, given that
1199 // the new top-layer will allow drawing to happen "below" it.
1200 SkRegion hole(ir);
1201 do {
1202 layer = layer->fNext;
1203 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1204 } while (layer->fNext);
1205 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206}
1207
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001208int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001209 if (0xFF == alpha) {
1210 return this->saveLayer(bounds, nullptr);
1211 } else {
1212 SkPaint tmpPaint;
1213 tmpPaint.setAlpha(alpha);
1214 return this->saveLayer(bounds, &tmpPaint);
1215 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001216}
1217
Mike Reed148b7fd2018-12-18 17:38:18 -05001218void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001219 SkBaseDevice* device = this->getTopDevice();
1220 if (nullptr == device) { // Do we still need this check???
1221 return;
1222 }
1223
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001224 // Map the local bounds into the top device's coordinate space (this is not
1225 // necessarily the full global CTM transform).
1226 SkIRect devBounds;
1227 if (localBounds) {
1228 SkRect tmp;
1229 device->localToDevice().mapRect(&tmp, *localBounds);
1230 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1231 devBounds.setEmpty();
1232 }
1233 } else {
1234 devBounds = device->devClipBounds();
1235 }
1236 if (devBounds.isEmpty()) {
1237 return;
1238 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001239
Michael Ludwigac352122019-08-28 21:03:05 +00001240 // This is getting the special image from the current device, which is then drawn into (both by
1241 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1242 // own device, we need to explicitly copy the back image contents so that its original content
1243 // is available when we splat it back later during restore.
1244 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001245 if (!backImage) {
1246 return;
1247 }
1248
1249 // we really need the save, so we can wack the fMCRec
1250 this->checkForDeferredSave();
1251
1252 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1253
1254 SkPaint paint;
1255 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001256 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001257}
1258
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259void SkCanvas::internalRestore() {
1260 SkASSERT(fMCStack.count() != 0);
1261
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001262 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 DeviceCM* layer = fMCRec->fLayer; // may be null
1264 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001265 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266
Mike Reed148b7fd2018-12-18 17:38:18 -05001267 // move this out before we do the actual restore
1268 auto backImage = std::move(fMCRec->fBackImage);
1269
Mike Reedeb1d5a22020-04-14 09:16:40 -04001270 fMarkerStack->restore(fMCRec);
Mike Reedb18e74d2020-01-16 13:58:22 -05001271
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 // now do the normal restore()
1273 fMCRec->~MCRec(); // balanced in save()
1274 fMCStack.pop_back();
1275 fMCRec = (MCRec*)fMCStack.back();
1276
Mike Reedc42a1cd2017-02-14 14:25:14 -05001277 if (fMCRec) {
1278 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1279 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001280
Mike Reed148b7fd2018-12-18 17:38:18 -05001281 if (backImage) {
1282 SkPaint paint;
1283 paint.setBlendMode(SkBlendMode::kDstOver);
1284 const int x = backImage->fLoc.x();
1285 const int y = backImage->fLoc.y();
1286 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1287 nullptr, SkMatrix::I());
1288 }
1289
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1291 since if we're being recorded, we don't want to record this (the
1292 recorder will have already recorded the restore).
1293 */
bsalomon49f085d2014-09-05 13:34:00 -07001294 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001295 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001296 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001297 // At this point, 'layer' has been removed from the device stack, so the devices that
1298 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1299 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001300 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001301 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001302 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001303 delete layer;
reedb679ca82015-04-07 04:40:48 -07001304 } else {
1305 // we're at the root
reeda499f902015-05-01 09:34:31 -07001306 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001307 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001308 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001310 }
msarettfbfa2582016-08-12 08:29:08 -07001311
1312 if (fMCRec) {
Brian Osmand8f611d2020-04-21 15:43:47 -04001313 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
msarettfbfa2582016-08-12 08:29:08 -07001314 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1315 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316}
1317
reede8f30622016-03-23 18:59:25 -07001318sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001319 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001320 props = &fProps;
1321 }
1322 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001323}
1324
reede8f30622016-03-23 18:59:25 -07001325sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001326 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001327 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001328}
1329
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001330SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001331 return this->onImageInfo();
1332}
1333
1334SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001335 SkBaseDevice* dev = this->getDevice();
1336 if (dev) {
1337 return dev->imageInfo();
1338 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001339 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001340 }
1341}
1342
brianosman898235c2016-04-06 07:38:23 -07001343bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001344 return this->onGetProps(props);
1345}
1346
1347bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001348 SkBaseDevice* dev = this->getDevice();
1349 if (dev) {
1350 if (props) {
1351 *props = fProps;
1352 }
1353 return true;
1354 } else {
1355 return false;
1356 }
1357}
1358
reed6ceeebd2016-03-09 14:26:26 -08001359bool SkCanvas::peekPixels(SkPixmap* pmap) {
1360 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001361}
1362
reed884e97c2015-05-26 11:31:54 -07001363bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001364 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001365 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001366}
1367
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001368void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001369 SkPixmap pmap;
1370 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001371 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001372 }
1373 if (info) {
1374 *info = pmap.info();
1375 }
1376 if (rowBytes) {
1377 *rowBytes = pmap.rowBytes();
1378 }
1379 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001380 // If the caller requested the origin, they presumably are expecting the returned pixels to
1381 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1382 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1383 // instead of an origin, just don't expose the pixels in that case. Note that this means
1384 // that layers with complex coordinate spaces can still report their pixels if the caller
1385 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1386 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1387 *origin = this->getTopDevice()->getOrigin();
1388 } else {
1389 return nullptr;
1390 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001391 }
reed884e97c2015-05-26 11:31:54 -07001392 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001393}
1394
reed884e97c2015-05-26 11:31:54 -07001395bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001396 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001397 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001398}
1399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401
Mike Reed8bcd1282019-03-13 16:51:54 -04001402// In our current design/features, we should never have a layer (src) in a different colorspace
1403// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1404// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1405// colorspace.
1406static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1407 SkASSERT(src == dst);
1408}
1409
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001410void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001411 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001413 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 paint = &tmp;
1415 }
reed@google.com4b226022011-01-11 18:32:13 +00001416
Mike Reed38992392019-07-30 10:48:15 -04001417 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001418
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001420 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001421 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1422 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001423 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001424 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001425 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1426 // so it should always be possible to use the relative origin. Once drawDevice() and
1427 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1428 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001429 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001430 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1431 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001432 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1433 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001434 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1435 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001436 }
reed@google.com76dd2772012-01-05 21:15:07 +00001437 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001438 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 }
reeda2217ef2016-07-20 06:04:34 -07001441
Mike Reed38992392019-07-30 10:48:15 -04001442 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443}
1444
reed32704672015-12-16 08:27:10 -08001445/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001446
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001447void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001448 if (dx || dy) {
1449 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001450 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001451
reedfe69b502016-09-12 06:31:48 -07001452 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001453 // However, if either is non-finite, we might still complicate the matrix type,
1454 // so we still have to compute this.
Brian Osmand8f611d2020-04-21 15:43:47 -04001455 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
mtkleincbdf0072016-08-19 09:05:27 -07001456
Mike Reedc42a1cd2017-02-14 14:25:14 -05001457 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001458
reedfe69b502016-09-12 06:31:48 -07001459 this->didTranslate(dx,dy);
1460 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001464 if (sx != 1 || sy != 1) {
1465 this->checkForDeferredSave();
1466 fMCRec->fMatrix.preScale(sx, sy);
1467
1468 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1469 // but pre-scaling by a non-finite does change it, so we have to recompute.
Brian Osmand8f611d2020-04-21 15:43:47 -04001470 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
Mike Reed9403c382020-01-13 14:40:56 +00001471
1472 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1473
1474 this->didScale(sx, sy);
1475 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476}
1477
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001478void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001479 SkMatrix m;
1480 m.setRotate(degrees);
1481 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482}
1483
bungeman7438bfc2016-07-12 15:01:19 -07001484void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1485 SkMatrix m;
1486 m.setRotate(degrees, px, py);
1487 this->concat(m);
1488}
1489
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001490void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001491 SkMatrix m;
1492 m.setSkew(sx, sy);
1493 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001494}
1495
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001496void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001497 if (matrix.isIdentity()) {
1498 return;
1499 }
1500
reed2ff1fce2014-12-11 07:07:37 -08001501 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001502 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001503
Brian Osmand8f611d2020-04-21 15:43:47 -04001504 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
Mike Reed7627fa52017-02-08 10:07:53 -05001505
Mike Reed7627fa52017-02-08 10:07:53 -05001506 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001507
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001508 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001509}
1510
Mike Reed2f92c932020-04-06 15:44:17 -04001511void SkCanvas::internalConcat44(const SkM44& m) {
Mike Reed403c8072020-01-08 10:40:39 -05001512 this->checkForDeferredSave();
1513
Mike Reed3ef77dd2020-04-06 10:41:09 -04001514 fMCRec->fMatrix.preConcat(m);
Mike Reed403c8072020-01-08 10:40:39 -05001515
Brian Osmand8f611d2020-04-21 15:43:47 -04001516 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
Mike Reed403c8072020-01-08 10:40:39 -05001517
1518 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed2f92c932020-04-06 15:44:17 -04001519}
Mike Reed403c8072020-01-08 10:40:39 -05001520
Mike Reed2f92c932020-04-06 15:44:17 -04001521void SkCanvas::concat(const SkM44& m) {
1522 this->internalConcat44(m);
Mike Reeda735ad92020-04-06 21:32:43 -04001523 // notify subclasses
Mike Reeda735ad92020-04-06 21:32:43 -04001524 this->didConcat44(m);
Mike Reedee3216d2020-01-17 17:35:04 -05001525}
1526
reed8c30a812016-04-20 16:36:51 -07001527void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
Brian Osmand8f611d2020-04-21 15:43:47 -04001528 fMCRec->fMatrix = SkM44(matrix);
msarett9da5a5a2016-08-19 08:38:36 -07001529 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001530
Mike Reedc42a1cd2017-02-14 14:25:14 -05001531 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001532}
1533
1534void SkCanvas::setMatrix(const SkMatrix& matrix) {
1535 this->checkForDeferredSave();
1536 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001537 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538}
1539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001541 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542}
1543
Brian Osman548de742020-04-24 12:02:25 -04001544void SkCanvas::markCTM(const char* name) {
Brian Osman84aa92e2020-04-30 10:43:33 -04001545 if (SkCanvasPriv::ValidateMarker(name)) {
Brian Osman548de742020-04-24 12:02:25 -04001546 fMarkerStack->setMarker(SkOpts::hash_fn(name, strlen(name), 0),
1547 this->getLocalToDevice(), fMCRec);
1548 this->onMarkCTM(name);
1549 }
Mike Reed7fe6ee32020-04-09 12:35:09 -04001550}
1551
Brian Osman548de742020-04-24 12:02:25 -04001552bool SkCanvas::findMarkedCTM(const char* name, SkM44* mx) const {
Brian Osman84aa92e2020-04-30 10:43:33 -04001553 return SkCanvasPriv::ValidateMarker(name) &&
1554 fMarkerStack->findMarker(SkOpts::hash_fn(name, strlen(name), 0), mx);
Mike Reed7fe6ee32020-04-09 12:35:09 -04001555}
1556
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557//////////////////////////////////////////////////////////////////////////////
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);
Brian Osmand8f611d2020-04-21 15:43:47 -04001574 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix.asM33(), this->getTopLayerBounds(),
1575 (SkRegion::Op)op, 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
Brian Osmand8f611d2020-04-21 15:43:47 -04001611 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix.asM33(), this->getTopLayerBounds(),
1612 (SkRegion::Op)op, 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
Brian Osmand8f611d2020-04-21 15:43:47 -04001620 if (!path.isInverseFillType() && fMCRec->fMatrix.asM33().rectStaysRect()) {
robertphillips39f05382015-11-24 09:30:12 -08001621 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;
Brian Osmand8f611d2020-04-21 15:43:47 -04001649 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix.asM33(), 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.
Brian Osmand8f611d2020-04-21 15:43:47 -04001791 SkASSERT(fIsScaleTranslate == SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix));
msarettfbfa2582016-08-12 08:29:08 -07001792#endif
1793
msarett9637ea92016-08-18 14:03:30 -07001794 if (!fIsScaleTranslate) {
Brian Osmand8f611d2020-04-21 15:43:47 -04001795 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix.asM33());
msarettfbfa2582016-08-12 08:29:08 -07001796 }
1797
1798 // We inline the implementation of mapScaleTranslate() for the fast path.
Brian Osmand8f611d2020-04-21 15:43:47 -04001799 float sx = fMCRec->fMatrix.rc(0, 0);
1800 float sy = fMCRec->fMatrix.rc(1, 1);
1801 float tx = fMCRec->fMatrix.rc(0, 3);
1802 float ty = fMCRec->fMatrix.rc(1, 3);
msarettfbfa2582016-08-12 08:29:08 -07001803 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
Mike Reed403c8072020-01-08 10:40:39 -05001852SkMatrix SkCanvas::getTotalMatrix() const {
Brian Osmand8f611d2020-04-21 15:43:47 -04001853 return fMCRec->fMatrix.asM33();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854}
1855
Mike Reed46f5c5f2020-02-20 15:42:29 -05001856SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001857 return fMCRec->fMatrix;
1858}
1859
Brian Osman11052242016-10-27 14:47:55 -04001860GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001861 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001862 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001863}
1864
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001865GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001866 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001867 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001868}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001869
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001870void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1871 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001872 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001873 if (outer.isEmpty()) {
1874 return;
1875 }
1876 if (inner.isEmpty()) {
1877 this->drawRRect(outer, paint);
1878 return;
1879 }
1880
1881 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001882 // be able to return ...
1883 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001884 //
1885 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001886 if (!outer.getBounds().contains(inner.getBounds())) {
1887 return;
1888 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001889
1890 this->onDrawDRRect(outer, inner, paint);
1891}
1892
reed41af9662015-01-05 07:49:08 -08001893void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001894 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001895 this->onDrawPaint(paint);
1896}
1897
1898void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001899 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001900 // To avoid redundant logic in our culling code and various backends, we always sort rects
1901 // before passing them along.
1902 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001903}
1904
Mike Reedd5674082019-04-19 15:00:47 -04001905void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1906 TRACE_EVENT0("skia", TRACE_FUNC);
1907 this->onDrawBehind(paint);
1908}
1909
msarettdca352e2016-08-26 06:37:45 -07001910void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001911 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001912 if (region.isEmpty()) {
1913 return;
1914 }
1915
1916 if (region.isRect()) {
1917 return this->drawIRect(region.getBounds(), paint);
1918 }
1919
1920 this->onDrawRegion(region, paint);
1921}
1922
reed41af9662015-01-05 07:49:08 -08001923void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001924 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001925 // To avoid redundant logic in our culling code and various backends, we always sort rects
1926 // before passing them along.
1927 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001928}
1929
1930void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001931 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001932 this->onDrawRRect(rrect, paint);
1933}
1934
1935void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001936 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001937 this->onDrawPoints(mode, count, pts, paint);
1938}
1939
Mike Reede88a1cb2017-03-17 09:50:46 -04001940void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1941 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001942 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001943}
1944
1945void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001946 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001947 RETURN_ON_NULL(vertices);
Brian Osmanf11e3312020-03-24 14:57:38 -04001948
Mike Reed5caf9352020-03-02 14:57:09 -05001949 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Brian Osman8cbedf92020-03-31 10:38:31 -04001950 SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);
Brian Osmanf11e3312020-03-24 14:57:38 -04001951
Brian Osmand1afef62020-04-09 16:24:23 -04001952 // If the vertices contain custom attributes, ensure they line up with the paint's shader.
Brian Osmanf11e3312020-03-24 14:57:38 -04001953 const SkRuntimeEffect* effect =
1954 paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001955 if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) {
Brian Osmanf11e3312020-03-24 14:57:38 -04001956 return;
1957 }
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001958 if (effect) {
1959 int attrIndex = 0;
1960 for (const auto& v : effect->varyings()) {
Brian Osmand1afef62020-04-09 16:24:23 -04001961 const SkVertices::Attribute& attr(vertices->priv().attributes()[attrIndex++]);
1962 // Mismatch between the SkSL varying and the vertex shader output for this attribute
1963 if (attr.channelCount() != v.fWidth) {
1964 return;
1965 }
1966 // If we can't provide any of the asked-for matrices, we can't draw this
Brian Osman548de742020-04-24 12:02:25 -04001967 if (attr.fMarkerID && !fMarkerStack->findMarker(attr.fMarkerID, nullptr)) {
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001968 return;
1969 }
1970 }
1971 }
Brian Osmanf11e3312020-03-24 14:57:38 -04001972
Mike Reed5caf9352020-03-02 14:57:09 -05001973 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001974}
1975
1976void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001977 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001978 this->onDrawPath(path, paint);
1979}
1980
reeda85d4d02015-05-06 12:56:48 -07001981void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001982 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001983 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001984 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001985}
1986
Mike Reedc4e31092018-01-30 11:15:27 -05001987// Returns true if the rect can be "filled" : non-empty and finite
1988static bool fillable(const SkRect& r) {
1989 SkScalar w = r.width();
1990 SkScalar h = r.height();
1991 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1992}
1993
reede47829b2015-08-06 10:02:53 -07001994void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1995 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001996 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001997 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001998 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001999 return;
2000 }
2001 this->onDrawImageRect(image, &src, dst, paint, constraint);
2002}
reed41af9662015-01-05 07:49:08 -08002003
reed84984ef2015-07-17 07:09:43 -07002004void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2005 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002006 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002007 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002008}
2009
Brian Salomonf08002c2018-10-26 16:15:46 -04002010void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002011 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002012 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002013 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002014}
reede47829b2015-08-06 10:02:53 -07002015
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002016namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002017class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002018public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002019 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2020 if (!origPaint) {
2021 return;
2022 }
2023 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2024 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2025 }
2026 if (origPaint->getMaskFilter()) {
2027 fPaint.writable()->setMaskFilter(nullptr);
2028 }
2029 if (origPaint->isAntiAlias()) {
2030 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002031 }
2032 }
2033
2034 const SkPaint* get() const {
2035 return fPaint;
2036 }
2037
2038private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002039 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002040};
2041} // namespace
2042
reed4c21dc52015-06-25 12:32:03 -07002043void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2044 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002045 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002046 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002047 if (dst.isEmpty()) {
2048 return;
2049 }
msarett552bca92016-08-03 06:53:26 -07002050 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002051 LatticePaint latticePaint(paint);
2052 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002053 } else {
reede47829b2015-08-06 10:02:53 -07002054 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002055 }
reed4c21dc52015-06-25 12:32:03 -07002056}
2057
msarett16882062016-08-16 09:31:08 -07002058void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2059 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002060 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002061 RETURN_ON_NULL(image);
2062 if (dst.isEmpty()) {
2063 return;
2064 }
msarett71df2d72016-09-30 12:41:42 -07002065
2066 SkIRect bounds;
2067 Lattice latticePlusBounds = lattice;
2068 if (!latticePlusBounds.fBounds) {
2069 bounds = SkIRect::MakeWH(image->width(), image->height());
2070 latticePlusBounds.fBounds = &bounds;
2071 }
2072
2073 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002074 LatticePaint latticePaint(paint);
2075 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002076 } else {
2077 this->drawImageRect(image, dst, paint);
2078 }
2079}
2080
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002081static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002082 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002083 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002084 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002085 return SkImage::MakeFromBitmap(bitmap);
2086}
2087
2088void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2089 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002090}
2091
reede47829b2015-08-06 10:02:53 -07002092void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002093 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002094 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002095}
2096
reed84984ef2015-07-17 07:09:43 -07002097void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2098 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002099 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002100}
2101
reede47829b2015-08-06 10:02:53 -07002102void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2103 SrcRectConstraint constraint) {
2104 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2105 constraint);
2106}
reede47829b2015-08-06 10:02:53 -07002107
reed71c3c762015-06-24 10:29:17 -07002108void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002109 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002110 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002111 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002112 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002113 if (count <= 0) {
2114 return;
2115 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002116 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002117 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002118 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002119}
2120
reedf70b5312016-03-04 16:36:20 -08002121void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002122 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002123 if (key) {
2124 this->onDrawAnnotation(rect, key, value);
2125 }
2126}
2127
reede47829b2015-08-06 10:02:53 -07002128void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2129 const SkPaint* paint, SrcRectConstraint constraint) {
2130 if (src) {
2131 this->drawImageRect(image, *src, dst, paint, constraint);
2132 } else {
2133 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2134 dst, paint, constraint);
2135 }
2136}
reede47829b2015-08-06 10:02:53 -07002137
Mike Reed4204da22017-05-17 08:53:36 -04002138void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002139 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002140 this->onDrawShadowRec(path, rec);
2141}
2142
2143void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2144 SkPaint paint;
2145 const SkRect& pathBounds = path.getBounds();
2146
Mike Reed38992392019-07-30 10:48:15 -04002147 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002148 while (iter.next()) {
2149 iter.fDevice->drawShadow(path, rec);
2150 }
Mike Reed38992392019-07-30 10:48:15 -04002151 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002152}
2153
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002154void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002155 QuadAAFlags aaFlags, const SkColor4f& color,
2156 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002157 TRACE_EVENT0("skia", TRACE_FUNC);
2158 // Make sure the rect is sorted before passing it along
2159 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2160}
2161
2162void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2163 const SkPoint dstClips[],
2164 const SkMatrix preViewMatrices[],
2165 const SkPaint* paint,
2166 SrcRectConstraint constraint) {
2167 TRACE_EVENT0("skia", TRACE_FUNC);
2168 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2169}
2170
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171//////////////////////////////////////////////////////////////////////////////
2172// These are the virtual drawing methods
2173//////////////////////////////////////////////////////////////////////////////
2174
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002175void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002176 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002177 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2178 }
2179}
2180
reed41af9662015-01-05 07:49:08 -08002181void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002182 this->internalDrawPaint(paint);
2183}
2184
2185void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002186 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187
2188 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002189 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 }
2191
Mike Reed38992392019-07-30 10:48:15 -04002192 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193}
2194
reed41af9662015-01-05 07:49:08 -08002195void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2196 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 if ((long)count <= 0) {
2198 return;
2199 }
2200
Mike Reed822128b2017-02-28 16:41:03 -05002201 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002202 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002203 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002204 // special-case 2 points (common for drawing a single line)
2205 if (2 == count) {
2206 r.set(pts[0], pts[1]);
2207 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002208 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002209 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002210 if (!r.isFinite()) {
2211 return;
2212 }
Mike Reed822128b2017-02-28 16:41:03 -05002213 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002214 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2215 return;
2216 }
2217 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002218 }
reed@google.coma584aed2012-05-16 14:06:02 +00002219
halcanary96fcdcc2015-08-27 07:41:13 -07002220 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221
Mike Reed38992392019-07-30 10:48:15 -04002222 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002223
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002225 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226 }
reed@google.com4b226022011-01-11 18:32:13 +00002227
Mike Reed38992392019-07-30 10:48:15 -04002228 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002229}
2230
reed4a167172016-08-18 17:15:25 -07002231static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002232 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002233}
2234
reed41af9662015-01-05 07:49:08 -08002235void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002236 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002238 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002239 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002240 return;
2241 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242 }
reed@google.com4b226022011-01-11 18:32:13 +00002243
reed4a167172016-08-18 17:15:25 -07002244 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002245 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246
reed4a167172016-08-18 17:15:25 -07002247 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002248 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002249 }
2250
Mike Reed38992392019-07-30 10:48:15 -04002251 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002252 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002253 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002254 SkDrawIter iter(this);
2255 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002256 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259}
2260
msarett44df6512016-08-25 13:54:30 -07002261void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002262 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002263 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002264 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002265 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2266 return;
2267 }
msarett44df6512016-08-25 13:54:30 -07002268 }
2269
Mike Reed38992392019-07-30 10:48:15 -04002270 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002271
2272 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002273 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002274 }
2275
Mike Reed38992392019-07-30 10:48:15 -04002276 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002277}
2278
Mike Reedd5674082019-04-19 15:00:47 -04002279void SkCanvas::onDrawBehind(const SkPaint& paint) {
2280 SkIRect bounds;
2281 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2282 for (;;) {
2283 const MCRec* rec = (const MCRec*)iter.prev();
2284 if (!rec) {
2285 return; // no backimages, so nothing to draw
2286 }
2287 if (rec->fBackImage) {
2288 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2289 rec->fBackImage->fImage->width(),
2290 rec->fBackImage->fImage->height());
2291 break;
2292 }
2293 }
2294
Mike Reed38992392019-07-30 10:48:15 -04002295 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002296
2297 while (iter.next()) {
2298 SkBaseDevice* dev = iter.fDevice;
2299
Mike Reedd5674082019-04-19 15:00:47 -04002300 dev->save();
2301 // We use clipRegion because it is already defined to operate in dev-space
2302 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2303 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002304 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002305 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002306 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002307 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002308 }
2309
Mike Reed38992392019-07-30 10:48:15 -04002310 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002311}
2312
reed41af9662015-01-05 07:49:08 -08002313void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002314 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002315 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002316 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002317 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002318 return;
2319 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002320 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002321
Mike Reed38992392019-07-30 10:48:15 -04002322 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002323
2324 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002325 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002326 }
2327
Mike Reed38992392019-07-30 10:48:15 -04002328 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002329}
2330
bsalomonac3aa242016-08-19 11:25:19 -07002331void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2332 SkScalar sweepAngle, bool useCenter,
2333 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002334 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002335 if (paint.canComputeFastBounds()) {
2336 SkRect storage;
2337 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002338 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002339 return;
2340 }
bsalomonac3aa242016-08-19 11:25:19 -07002341 }
2342
Mike Reed38992392019-07-30 10:48:15 -04002343 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002344
2345 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002346 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002347 }
2348
Mike Reed38992392019-07-30 10:48:15 -04002349 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002350}
2351
reed41af9662015-01-05 07:49:08 -08002352void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002353 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002354 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002355 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2356 return;
2357 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002358 }
2359
2360 if (rrect.isRect()) {
2361 // call the non-virtual version
2362 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002363 return;
2364 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002365 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002366 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2367 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002368 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002369
Mike Reed38992392019-07-30 10:48:15 -04002370 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002371
2372 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002373 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002374 }
2375
Mike Reed38992392019-07-30 10:48:15 -04002376 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002377}
2378
Mike Reed822128b2017-02-28 16:41:03 -05002379void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002380 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002381 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002382 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2383 return;
2384 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002385 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002386
Mike Reed38992392019-07-30 10:48:15 -04002387 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002388
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002389 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002390 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002391 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002392
Mike Reed38992392019-07-30 10:48:15 -04002393 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002394}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002395
reed41af9662015-01-05 07:49:08 -08002396void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002397 if (!path.isFinite()) {
2398 return;
2399 }
2400
Mike Reed822128b2017-02-28 16:41:03 -05002401 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002402 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002403 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002404 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2405 return;
2406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002408
Mike Reed822128b2017-02-28 16:41:03 -05002409 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002410 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002411 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002412 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002413 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002414 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002415
Mike Reed38992392019-07-30 10:48:15 -04002416 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417
2418 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002419 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002420 }
2421
Mike Reed38992392019-07-30 10:48:15 -04002422 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423}
2424
reed262a71b2015-12-05 13:07:27 -08002425bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002426 if (!paint.getImageFilter()) {
2427 return false;
2428 }
2429
2430 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002431 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002432 return false;
2433 }
2434
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002435 // The other paint effects need to be applied before the image filter, but the sprite draw
2436 // applies the filter explicitly first.
2437 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2438 return false;
2439 }
reed262a71b2015-12-05 13:07:27 -08002440 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2441 // Once we can filter and the filter will return a result larger than itself, we should be
2442 // able to remove this constraint.
2443 // skbug.com/4526
2444 //
2445 SkPoint pt;
2446 ctm.mapXY(x, y, &pt);
2447 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2448 return ir.contains(fMCRec->fRasterClip.getBounds());
2449}
2450
Mike Reedf441cfc2018-04-11 14:50:16 -04002451// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2452// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2453// null.
2454static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2455 if (paintParam) {
2456 *real = *paintParam;
2457 real->setStyle(SkPaint::kFill_Style);
2458 real->setPathEffect(nullptr);
2459 paintParam = real;
2460 }
2461 return paintParam;
2462}
2463
reeda85d4d02015-05-06 12:56:48 -07002464void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002465 SkPaint realPaint;
2466 paint = init_image_paint(&realPaint, paint);
2467
reeda85d4d02015-05-06 12:56:48 -07002468 SkRect bounds = SkRect::MakeXYWH(x, y,
2469 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002470 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002471 SkRect tmp = bounds;
2472 if (paint) {
2473 paint->computeFastBounds(tmp, &tmp);
2474 }
2475 if (this->quickReject(tmp)) {
2476 return;
2477 }
reeda85d4d02015-05-06 12:56:48 -07002478 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002479 // At this point we need a real paint object. If the caller passed null, then we should
2480 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2481 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2482 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002483
reeda2217ef2016-07-20 06:04:34 -07002484 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002485 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2486 *paint);
2487 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002488 special = this->getDevice()->makeSpecial(image);
2489 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002490 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002491 }
2492 }
2493
Mike Reed38992392019-07-30 10:48:15 -04002494 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002495
reeda85d4d02015-05-06 12:56:48 -07002496 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002497 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002498 if (special) {
2499 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002500 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002501 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002502 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002503 SkScalarRoundToInt(pt.fY), pnt,
2504 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002505 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002506 iter.fDevice->drawImageRect(
2507 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2508 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002509 }
reeda85d4d02015-05-06 12:56:48 -07002510 }
halcanary9d524f22016-03-29 09:03:52 -07002511
Mike Reed38992392019-07-30 10:48:15 -04002512 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002513}
2514
reed41af9662015-01-05 07:49:08 -08002515void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002516 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002517 SkPaint realPaint;
2518 paint = init_image_paint(&realPaint, paint);
2519
halcanary96fcdcc2015-08-27 07:41:13 -07002520 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002521 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002522 if (paint) {
2523 paint->computeFastBounds(dst, &storage);
2524 }
2525 if (this->quickReject(storage)) {
2526 return;
2527 }
reeda85d4d02015-05-06 12:56:48 -07002528 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002529 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002530
Mike Reed38992392019-07-30 10:48:15 -04002531 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002532
reeda85d4d02015-05-06 12:56:48 -07002533 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002534 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002535 }
halcanary9d524f22016-03-29 09:03:52 -07002536
Mike Reed38992392019-07-30 10:48:15 -04002537 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002538}
2539
reed4c21dc52015-06-25 12:32:03 -07002540void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2541 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002542 SkPaint realPaint;
2543 paint = init_image_paint(&realPaint, paint);
2544
halcanary96fcdcc2015-08-27 07:41:13 -07002545 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002546 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002547 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2548 return;
2549 }
reed@google.com3d608122011-11-21 15:16:16 +00002550 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002551 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002552
Mike Reed38992392019-07-30 10:48:15 -04002553 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002554
reed4c21dc52015-06-25 12:32:03 -07002555 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002556 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002557 }
halcanary9d524f22016-03-29 09:03:52 -07002558
Mike Reed38992392019-07-30 10:48:15 -04002559 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002560}
2561
msarett16882062016-08-16 09:31:08 -07002562void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2563 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002564 SkPaint realPaint;
2565 paint = init_image_paint(&realPaint, paint);
2566
msarett16882062016-08-16 09:31:08 -07002567 if (nullptr == paint || paint->canComputeFastBounds()) {
2568 SkRect storage;
2569 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2570 return;
2571 }
2572 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002573 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002574
Mike Reed38992392019-07-30 10:48:15 -04002575 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002576
2577 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002578 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002579 }
2580
Mike Reed38992392019-07-30 10:48:15 -04002581 DRAW_END
msarett16882062016-08-16 09:31:08 -07002582}
2583
fmalita00d5c2c2014-08-21 08:53:26 -07002584void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2585 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002586 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002587 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002588 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002589 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002590 SkRect tmp;
2591 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2592 return;
2593 }
2594 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002595 }
2596
fmalita024f9962015-03-03 19:08:17 -08002597 // We cannot filter in the looper as we normally do, because the paint is
2598 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002599 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002600
fmalitaaa1b9122014-08-28 14:32:24 -07002601 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002602 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002603 }
2604
Mike Reed38992392019-07-30 10:48:15 -04002605 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002606}
2607
Mike Reed358fcad2018-11-23 15:27:51 -05002608// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002609void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002610 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2611 TRACE_EVENT0("skia", TRACE_FUNC);
2612 if (byteLength) {
2613 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002614 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002615 }
2616}
Mike Reed4f81bb72019-01-23 09:23:00 -05002617
fmalita00d5c2c2014-08-21 08:53:26 -07002618void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2619 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002620 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002621 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002622 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002623 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002624}
reed@google.come0d9ce82014-04-23 04:00:17 +00002625
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002626void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2627 const SkPaint& paint) {
2628 DRAW_BEGIN(paint, nullptr)
2629
2630 while (iter.next()) {
2631 // In the common case of one iteration we could std::move vertices here.
2632 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2633 }
2634
2635 DRAW_END
2636}
Brian Salomon199fb872017-02-06 09:41:10 -05002637
dandovb3c9d1c2014-08-12 08:34:29 -07002638void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002639 const SkPoint texCoords[4], SkBlendMode bmode,
2640 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002641 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002642 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002643 return;
2644 }
mtklein6cfa73a2014-08-13 13:33:49 -07002645
Mike Reedfaba3712016-11-03 14:45:31 -04002646 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002647}
2648
2649void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002650 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002651 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002652 // Since a patch is always within the convex hull of the control points, we discard it when its
2653 // bounding rectangle is completely outside the current clip.
2654 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002655 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002656 if (this->quickReject(bounds)) {
2657 return;
2658 }
mtklein6cfa73a2014-08-13 13:33:49 -07002659
Mike Reed38992392019-07-30 10:48:15 -04002660 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002661
dandovecfff212014-08-04 10:02:00 -07002662 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002663 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002664 }
mtklein6cfa73a2014-08-13 13:33:49 -07002665
Mike Reed38992392019-07-30 10:48:15 -04002666 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002667}
2668
reeda8db7282015-07-07 10:22:31 -07002669void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002670#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002671 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002672#endif
reede3b38ce2016-01-08 09:18:44 -08002673 RETURN_ON_NULL(dr);
2674 if (x || y) {
2675 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2676 this->onDrawDrawable(dr, &matrix);
2677 } else {
2678 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002679 }
2680}
2681
reeda8db7282015-07-07 10:22:31 -07002682void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002683#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002684 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002685#endif
reede3b38ce2016-01-08 09:18:44 -08002686 RETURN_ON_NULL(dr);
2687 if (matrix && matrix->isIdentity()) {
2688 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002689 }
reede3b38ce2016-01-08 09:18:44 -08002690 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002691}
2692
2693void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002694 // drawable bounds are no longer reliable (e.g. android displaylist)
2695 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002696 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002697}
2698
reed71c3c762015-06-24 10:29:17 -07002699void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002700 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002701 const SkRect* cull, const SkPaint* paint) {
2702 if (cull && this->quickReject(*cull)) {
2703 return;
2704 }
2705
2706 SkPaint pnt;
2707 if (paint) {
2708 pnt = *paint;
2709 }
halcanary9d524f22016-03-29 09:03:52 -07002710
Mike Reed38992392019-07-30 10:48:15 -04002711 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002712 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002713 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002714 }
Mike Reed38992392019-07-30 10:48:15 -04002715 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002716}
2717
reedf70b5312016-03-04 16:36:20 -08002718void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2719 SkASSERT(key);
2720
2721 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002722 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002723 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002724 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002725 }
Mike Reed38992392019-07-30 10:48:15 -04002726 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002727}
2728
Michael Ludwiga595f862019-08-27 15:25:49 -04002729void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2730 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002731 SkASSERT(r.isSorted());
2732
2733 // If this used a paint, it would be a filled color with blend mode, which does not
2734 // need to use an autodraw loop, so use SkDrawIter directly.
2735 if (this->quickReject(r)) {
2736 return;
2737 }
2738
Michael Ludwiga4b44882019-08-28 14:34:58 -04002739 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002740 SkDrawIter iter(this);
2741 while(iter.next()) {
2742 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2743 }
2744}
2745
2746void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2747 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2748 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002749 if (count <= 0) {
2750 // Nothing to draw
2751 return;
2752 }
2753
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002754 SkPaint realPaint;
2755 init_image_paint(&realPaint, paint);
2756
Michael Ludwiga4b44882019-08-28 14:34:58 -04002757 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2758 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2759 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2760 // or we need it for the autolooper (since it greatly improves image filter perf).
2761 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2762 bool setBoundsValid = count == 1 || needsAutoLooper;
2763 SkRect setBounds = imageSet[0].fDstRect;
2764 if (imageSet[0].fMatrixIndex >= 0) {
2765 // Account for the per-entry transform that is applied prior to the CTM when drawing
2766 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002767 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002768 if (needsAutoLooper) {
2769 for (int i = 1; i < count; ++i) {
2770 SkRect entryBounds = imageSet[i].fDstRect;
2771 if (imageSet[i].fMatrixIndex >= 0) {
2772 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2773 }
2774 setBounds.joinPossiblyEmptyRect(entryBounds);
2775 }
2776 }
2777
2778 // If we happen to have the draw bounds, though, might as well check quickReject().
2779 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2780 SkRect tmp;
2781 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2782 return;
2783 }
2784 }
2785
2786 if (needsAutoLooper) {
2787 SkASSERT(setBoundsValid);
2788 DRAW_BEGIN(realPaint, &setBounds)
2789 while (iter.next()) {
2790 iter.fDevice->drawEdgeAAImageSet(
2791 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2792 }
2793 DRAW_END
2794 } else {
2795 this->predrawNotify();
2796 SkDrawIter iter(this);
2797 while(iter.next()) {
2798 iter.fDevice->drawEdgeAAImageSet(
2799 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2800 }
2801 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002802}
2803
reed@android.com8a1c16f2008-12-17 15:59:43 +00002804//////////////////////////////////////////////////////////////////////////////
2805// These methods are NOT virtual, and therefore must call back into virtual
2806// methods, rather than actually drawing themselves.
2807//////////////////////////////////////////////////////////////////////////////
2808
reed374772b2016-10-05 17:33:02 -07002809void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002810 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002811 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002812 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002813 this->drawPaint(paint);
2814}
2815
2816void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002817 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002818 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2819}
2820
Mike Reed3661bc92017-02-22 13:21:42 -05002821void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002822 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002823 pts[0].set(x0, y0);
2824 pts[1].set(x1, y1);
2825 this->drawPoints(kLines_PointMode, 2, pts, paint);
2826}
2827
Mike Reed3661bc92017-02-22 13:21:42 -05002828void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002829 if (radius < 0) {
2830 radius = 0;
2831 }
2832
2833 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002834 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002835 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002836}
2837
2838void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2839 const SkPaint& paint) {
2840 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002841 SkRRect rrect;
2842 rrect.setRectXY(r, rx, ry);
2843 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002844 } else {
2845 this->drawRect(r, paint);
2846 }
2847}
2848
reed@android.com8a1c16f2008-12-17 15:59:43 +00002849void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2850 SkScalar sweepAngle, bool useCenter,
2851 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002852 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002853 if (oval.isEmpty() || !sweepAngle) {
2854 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002855 }
bsalomon21af9ca2016-08-25 12:29:23 -07002856 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002857}
2858
reed@android.comf76bacf2009-05-13 14:00:33 +00002859///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002860#ifdef SK_DISABLE_SKPICTURE
2861void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002862
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002863
2864void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2865 const SkPaint* paint) {}
2866#else
Mike Klein88d90712018-01-27 17:30:04 +00002867/**
2868 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2869 * against the playback cost of recursing into the subpicture to get at its actual ops.
2870 *
2871 * For now we pick a conservatively small value, though measurement (and other heuristics like
2872 * the type of ops contained) may justify changing this value.
2873 */
2874#define kMaxPictureOpsToUnrollInsteadOfRef 1
2875
reedd5fa1a42014-08-09 11:08:05 -07002876void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002877 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002878 RETURN_ON_NULL(picture);
2879
reede3b38ce2016-01-08 09:18:44 -08002880 if (matrix && matrix->isIdentity()) {
2881 matrix = nullptr;
2882 }
Mike Klein88d90712018-01-27 17:30:04 +00002883 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2884 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2885 picture->playback(this);
2886 } else {
2887 this->onDrawPicture(picture, matrix, paint);
2888 }
reedd5fa1a42014-08-09 11:08:05 -07002889}
robertphillips9b14f262014-06-04 05:40:44 -07002890
reedd5fa1a42014-08-09 11:08:05 -07002891void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2892 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002893 if (!paint || paint->canComputeFastBounds()) {
2894 SkRect bounds = picture->cullRect();
2895 if (paint) {
2896 paint->computeFastBounds(bounds, &bounds);
2897 }
2898 if (matrix) {
2899 matrix->mapRect(&bounds);
2900 }
2901 if (this->quickReject(bounds)) {
2902 return;
2903 }
2904 }
2905
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002906 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002907 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002908}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002909#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002910
reed@android.com8a1c16f2008-12-17 15:59:43 +00002911///////////////////////////////////////////////////////////////////////////////
2912///////////////////////////////////////////////////////////////////////////////
2913
reed3aafe112016-08-18 12:45:34 -07002914SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002915 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002916
2917 SkASSERT(canvas);
2918
reed3aafe112016-08-18 12:45:34 -07002919 fImpl = new (fStorage) SkDrawIter(canvas);
Michael Ludwig2a8a3ff2020-03-26 15:33:20 -04002920 // This advances the base iterator to the first device and caches its origin,
2921 // correctly handling the case where there are no devices.
2922 this->next();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002923}
2924
2925SkCanvas::LayerIter::~LayerIter() {
2926 fImpl->~SkDrawIter();
2927}
2928
2929void SkCanvas::LayerIter::next() {
2930 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002931 if (!fDone) {
2932 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2933 // filters, so its devices will always be able to report the origin exactly.
2934 fDeviceOrigin = fImpl->fDevice->getOrigin();
2935 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002936}
2937
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002938SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002939 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940}
2941
2942const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002943 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944}
2945
2946const SkPaint& SkCanvas::LayerIter::paint() const {
2947 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002948 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949 paint = &fDefaultPaint;
2950 }
2951 return *paint;
2952}
2953
Mike Reedca37f322018-03-08 13:22:16 -05002954SkIRect SkCanvas::LayerIter::clipBounds() const {
2955 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002956}
2957
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002958int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2959int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002960
2961///////////////////////////////////////////////////////////////////////////////
2962
Brian Osmane8a98632019-04-10 10:26:10 -04002963SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2964SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2965SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2966SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
2967
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002968SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2969 const SkRect& dstRect, int matrixIndex, float alpha,
2970 unsigned aaFlags, bool hasClip)
2971 : fImage(std::move(image))
2972 , fSrcRect(srcRect)
2973 , fDstRect(dstRect)
2974 , fMatrixIndex(matrixIndex)
2975 , fAlpha(alpha)
2976 , fAAFlags(aaFlags)
2977 , fHasClip(hasClip) {}
2978
2979SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2980 const SkRect& dstRect, float alpha, unsigned aaFlags)
2981 : fImage(std::move(image))
2982 , fSrcRect(srcRect)
2983 , fDstRect(dstRect)
2984 , fAlpha(alpha)
2985 , fAAFlags(aaFlags) {}
2986
2987///////////////////////////////////////////////////////////////////////////////
2988
Mike Reed5df49342016-11-12 08:06:55 -06002989std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05002990 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04002991 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002992 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002993 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002994
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002995 SkBitmap bitmap;
2996 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002997 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002998 }
Mike Reed12f77342017-11-08 11:19:52 -05002999
3000 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003001 std::make_unique<SkCanvas>(bitmap, *props) :
3002 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003003}
reedd5fa1a42014-08-09 11:08:05 -07003004
3005///////////////////////////////////////////////////////////////////////////////
3006
Florin Malitaee424ac2016-12-01 12:47:59 -05003007SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003008 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003009
Florin Malita439ace92016-12-02 12:05:41 -05003010SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003011 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003012
Herb Derbyefe39bc2018-05-01 17:06:20 -04003013SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003014 : INHERITED(device) {}
3015
Florin Malitaee424ac2016-12-01 12:47:59 -05003016SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3017 (void)this->INHERITED::getSaveLayerStrategy(rec);
3018 return kNoLayer_SaveLayerStrategy;
3019}
3020
Mike Reed148b7fd2018-12-18 17:38:18 -05003021bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3022 return false;
3023}
3024
Florin Malitaee424ac2016-12-01 12:47:59 -05003025///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003026
reed73603f32016-09-20 08:42:38 -07003027static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3028static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3029static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3030static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3031static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3032static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003033
3034///////////////////////////////////////////////////////////////////////////////////////////////////
3035
3036SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3037 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003038 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003039 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003040 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003041 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003042 clip.setEmpty();
3043 }
3044
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003045 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003046 return handle;
3047 }
3048 return nullptr;
3049}
3050
3051static bool install(SkBitmap* bm, const SkImageInfo& info,
3052 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003053 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003054}
3055
3056SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3057 SkBitmap* bm) {
3058 SkRasterHandleAllocator::Rec rec;
3059 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3060 return nullptr;
3061 }
3062 return rec.fHandle;
3063}
3064
3065std::unique_ptr<SkCanvas>
3066SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3067 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003068 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003069 return nullptr;
3070 }
3071
3072 SkBitmap bm;
3073 Handle hndl;
3074
3075 if (rec) {
3076 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3077 } else {
3078 hndl = alloc->allocBitmap(info, &bm);
3079 }
3080 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3081}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003082
3083///////////////////////////////////////////////////////////////////////////////////////////////////