blob: c6ba1589312d7323481dd6a32cbe812cd9e4bcd4 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Brian Osmanf11e3312020-03-24 14:57:38 -040020#include "include/effects/SkRuntimeEffect.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/private/SkNx.h"
22#include "include/private/SkTo.h"
23#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040024#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050026#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/core/SkCanvasPriv.h"
28#include "src/core/SkClipOpPriv.h"
29#include "src/core/SkClipStack.h"
30#include "src/core/SkDraw.h"
31#include "src/core/SkGlyphRun.h"
32#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040033#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050034#include "src/core/SkLatticeIter.h"
35#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050036#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "src/core/SkMatrixUtils.h"
38#include "src/core/SkPaintPriv.h"
39#include "src/core/SkRasterClip.h"
40#include "src/core/SkSpecialImage.h"
41#include "src/core/SkStrikeCache.h"
42#include "src/core/SkTLazy.h"
43#include "src/core/SkTextFormatParams.h"
44#include "src/core/SkTraceEvent.h"
Mike Reedba962562020-03-12 20:33:21 -040045#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050046#include "src/image/SkImage_Base.h"
47#include "src/image/SkSurface_Base.h"
48#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040049
bungemand3ebb482015-08-05 13:57:49 -070050#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050053#include "include/gpu/GrContext.h"
54#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000055#endif
56
reede3b38ce2016-01-08 09:18:44 -080057#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050058#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080059
Mike Klein1bb7e232019-12-10 08:58:52 -060060// This is a test: static_assert with no message is a c++17 feature,
61// and std::max() is constexpr only since the c++14 stdlib.
62static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000063
Mike Reed139e5e02017-03-08 11:29:33 -050064///////////////////////////////////////////////////////////////////////////////////////////////////
65
reedc83a2972015-07-16 07:40:45 -070066/*
67 * Return true if the drawing this rect would hit every pixels in the canvas.
68 *
69 * Returns false if
70 * - rect does not contain the canvas' bounds
71 * - paint is not fill
72 * - paint would blur or otherwise change the coverage of the rect
73 */
74bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
75 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070076 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
77 (int)kNone_ShaderOverrideOpacity,
78 "need_matching_enums0");
79 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
80 (int)kOpaque_ShaderOverrideOpacity,
81 "need_matching_enums1");
82 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
83 (int)kNotOpaque_ShaderOverrideOpacity,
84 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070085
86 const SkISize size = this->getBaseLayerSize();
87 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050088
89 // if we're clipped at all, we can't overwrite the entire surface
90 {
91 SkBaseDevice* base = this->getDevice();
92 SkBaseDevice* top = this->getTopDevice();
93 if (base != top) {
94 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
95 }
96 if (!base->clipIsWideOpen()) {
97 return false;
98 }
reedc83a2972015-07-16 07:40:45 -070099 }
100
101 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700102 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700103 return false; // conservative
104 }
halcanaryc5769b22016-08-10 07:13:21 -0700105
106 SkRect devRect;
107 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
108 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700109 return false;
110 }
111 }
112
113 if (paint) {
114 SkPaint::Style paintStyle = paint->getStyle();
115 if (!(paintStyle == SkPaint::kFill_Style ||
116 paintStyle == SkPaint::kStrokeAndFill_Style)) {
117 return false;
118 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400119 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700120 return false; // conservative
121 }
122 }
123 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
124}
125
126///////////////////////////////////////////////////////////////////////////////////////////////////
127
reed@google.comda17f752012-08-16 18:27:05 +0000128// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129//#define SK_TRACE_SAVERESTORE
130
131#ifdef SK_TRACE_SAVERESTORE
132 static int gLayerCounter;
133 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
134 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
135
136 static int gRecCounter;
137 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
138 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
139
140 static int gCanvasCounter;
141 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
142 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
143#else
144 #define inc_layer()
145 #define dec_layer()
146 #define inc_rec()
147 #define dec_rec()
148 #define inc_canvas()
149 #define dec_canvas()
150#endif
151
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000152typedef SkTLazy<SkPaint> SkLazyPaint;
153
reedc83a2972015-07-16 07:40:45 -0700154void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000155 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700156 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
157 ? SkSurface::kDiscard_ContentChangeMode
158 : SkSurface::kRetain_ContentChangeMode);
159 }
160}
161
162void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
163 ShaderOverrideOpacity overrideOpacity) {
164 if (fSurfaceBase) {
165 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
166 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
167 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
168 // and therefore we don't care which mode we're in.
169 //
170 if (fSurfaceBase->outstandingImageSnapshot()) {
171 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
172 mode = SkSurface::kDiscard_ContentChangeMode;
173 }
174 }
175 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000176 }
177}
178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000181/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 The clip/matrix/proc are fields that reflect the top of the save/restore
183 stack. Whenever the canvas changes, it marks a dirty flag, and then before
184 these are used (assuming we're not on a layer) we rebuild these cache
185 values: they reflect the top of the save stack, but translated and clipped
186 by the device's XY offset and bitmap-bounds.
187*/
188struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400189 DeviceCM* fNext;
190 sk_sp<SkBaseDevice> fDevice;
191 SkRasterClip fClip;
192 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
193 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400194 sk_sp<SkImage> fClipImage;
195 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196
Florin Malita53f77bd2017-04-28 13:48:37 -0400197 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000198 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700199 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400200 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500201 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700202 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000203 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400204 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400205 {}
reed@google.com4b226022011-01-11 18:32:13 +0000206
mtkleinfeaadee2015-04-08 11:25:48 -0700207 void reset(const SkIRect& bounds) {
208 SkASSERT(!fPaint);
209 SkASSERT(!fNext);
210 SkASSERT(fDevice);
211 fClip.setRect(bounds);
212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213};
214
Mike Reed148b7fd2018-12-18 17:38:18 -0500215namespace {
216// Encapsulate state needed to restore from saveBehind()
217struct BackImage {
218 sk_sp<SkSpecialImage> fImage;
219 SkIPoint fLoc;
220};
221}
222
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223/* This is the record we keep for each save/restore level in the stack.
224 Since a level optionally copies the matrix and/or stack, we have pointers
225 for these fields. If the value is copied for this level, the copy is
226 stored in the ...Storage field, and the pointer points to that. If the
227 value is not copied for this level, we ignore ...Storage, and just point
228 at the corresponding value in the previous level in the stack.
229*/
230class SkCanvas::MCRec {
231public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500232 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 /* If there are any layers in the stack, this points to the top-most
234 one that is at or below this level in the stack (so we know what
235 bitmap/device to draw into from this level. This value is NOT
236 reference counted, since the real owner is either our fLayer field,
237 or a previous one in a lower level.)
238 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500239 DeviceCM* fTopLayer;
240 std::unique_ptr<BackImage> fBackImage;
241 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500242 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500243 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
Mike Reeda1361362017-03-07 09:37:29 -0500245 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700246 fLayer = nullptr;
247 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800248 fMatrix.reset();
249 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700250
reedd9544982014-09-09 18:46:22 -0700251 // don't bother initializing fNext
252 inc_rec();
253 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400254 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700255 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700256 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800257 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 // don't bother initializing fNext
260 inc_rec();
261 }
262 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700263 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 dec_rec();
265 }
mtkleinfeaadee2015-04-08 11:25:48 -0700266
267 void reset(const SkIRect& bounds) {
268 SkASSERT(fLayer);
269 SkASSERT(fDeferredSaveCount == 0);
270
271 fMatrix.reset();
272 fRasterClip.setRect(bounds);
273 fLayer->reset(bounds);
274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275};
276
Mike Reeda1361362017-03-07 09:37:29 -0500277class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278public:
Mike Reeda1361362017-03-07 09:37:29 -0500279 SkDrawIter(SkCanvas* canvas)
280 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
281 {}
reed@google.com4b226022011-01-11 18:32:13 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000284 const DeviceCM* rec = fCurrLayer;
285 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400286 fDevice = rec->fDevice.get();
287 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700289 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 return true;
291 }
292 return false;
293 }
reed@google.com4b226022011-01-11 18:32:13 +0000294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000296
Mike Reed99330ba2017-02-22 11:01:08 -0500297 SkBaseDevice* fDevice;
298
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 const DeviceCM* fCurrLayer;
301 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302};
303
Florin Malita713b8ef2017-04-28 10:57:24 -0400304#define FOR_EACH_TOP_DEVICE( code ) \
305 do { \
306 DeviceCM* layer = fMCRec->fTopLayer; \
307 while (layer) { \
308 SkBaseDevice* device = layer->fDevice.get(); \
309 if (device) { \
310 code; \
311 } \
312 layer = layer->fNext; \
313 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500314 } while (0)
315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316/////////////////////////////////////////////////////////////////////////////
317
reeddbc3cef2015-04-29 12:18:57 -0700318/**
319 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700320 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700321 */
reedd053ce92016-03-22 10:17:23 -0700322static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700323 SkImageFilter* imgf = paint.getImageFilter();
324 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700325 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700326 }
327
reedd053ce92016-03-22 10:17:23 -0700328 SkColorFilter* imgCFPtr;
329 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700330 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700331 }
reedd053ce92016-03-22 10:17:23 -0700332 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700333
334 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700335 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700336 // there is no existing paint colorfilter, so we can just return the imagefilter's
337 return imgCF;
338 }
339
340 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
341 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500342 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700343}
344
senorblanco87e066e2015-10-28 11:23:36 -0700345/**
346 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
347 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
348 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
349 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
350 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
351 * conservative "effective" bounds based on the settings in the paint... with one exception. This
352 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
353 * deliberately ignored.
354 */
355static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
356 const SkRect& rawBounds,
357 SkRect* storage) {
358 SkPaint tmpUnfiltered(paint);
359 tmpUnfiltered.setImageFilter(nullptr);
360 if (tmpUnfiltered.canComputeFastBounds()) {
361 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
362 } else {
363 return rawBounds;
364 }
365}
366
Mike Reed38992392019-07-30 10:48:15 -0400367class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368public:
senorblanco87e066e2015-10-28 11:23:36 -0700369 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
370 // paint. It's used to determine the size of the offscreen layer for filters.
371 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400372 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
373 bool skipLayerForImageFilter = false,
374 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400376 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000377 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700378 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
Mike Reed38992392019-07-30 10:48:15 -0400380 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
381 SkASSERT(!fLazyPaint.isValid());
382 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700383 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700384 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700385 fPaint = paint;
386 }
387
388 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700389 /**
390 * We implement ImageFilters for a given draw by creating a layer, then applying the
391 * imagefilter to the pixels of that layer (its backing surface/image), and then
392 * we call restore() to xfer that layer to the main canvas.
393 *
394 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
395 * 2. Generate the src pixels:
396 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
397 * return (fPaint). We then draw the primitive (using srcover) into a cleared
398 * buffer/surface.
399 * 3. Restore the layer created in #1
400 * The imagefilter is passed the buffer/surface from the layer (now filled with the
401 * src pixels of the primitive). It returns a new "filtered" buffer, which we
402 * draw onto the previous layer using the xfermode from the original paint.
403 */
Mike Reed38992392019-07-30 10:48:15 -0400404
405 SkPaint restorePaint;
406 restorePaint.setImageFilter(fPaint->refImageFilter());
407 restorePaint.setBlendMode(fPaint->getBlendMode());
408
senorblanco87e066e2015-10-28 11:23:36 -0700409 SkRect storage;
410 if (rawBounds) {
411 // Make rawBounds include all paint outsets except for those due to image filters.
412 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
413 }
Mike Reed38992392019-07-30 10:48:15 -0400414 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700415 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700416 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000417
Mike Reed38992392019-07-30 10:48:15 -0400418 // Remove the restorePaint fields from our "working" paint
419 SkASSERT(!fLazyPaint.isValid());
420 SkPaint* paint = fLazyPaint.set(origPaint);
421 paint->setImageFilter(nullptr);
422 paint->setBlendMode(SkBlendMode::kSrcOver);
423 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000424 }
425 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000426
Mike Reed38992392019-07-30 10:48:15 -0400427 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700428 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000429 fCanvas->internalRestore();
430 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000431 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000433
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434 const SkPaint& paint() const {
435 SkASSERT(fPaint);
436 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439private:
Mike Reed38992392019-07-30 10:48:15 -0400440 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400442 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000443 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700444 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445};
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447////////// macros to place around the internal draw calls //////////////////
448
Mike Reed38992392019-07-30 10:48:15 -0400449#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700450 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400451 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
452 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800453
454
Mike Reed38992392019-07-30 10:48:15 -0400455#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000456 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400457 AutoLayerForImageFilter draw(this, paint, true); \
458 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000459
Mike Reed38992392019-07-30 10:48:15 -0400460#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000461 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400462 AutoLayerForImageFilter draw(this, paint, false, bounds); \
463 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000464
Mike Reed38992392019-07-30 10:48:15 -0400465#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700466 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400467 AutoLayerForImageFilter draw(this, paint, false, bounds); \
468 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700469
Mike Reed38992392019-07-30 10:48:15 -0400470#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471
472////////////////////////////////////////////////////////////////////////////
473
msarettfbfa2582016-08-12 08:29:08 -0700474static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
475 if (bounds.isEmpty()) {
476 return SkRect::MakeEmpty();
477 }
478
479 // Expand bounds out by 1 in case we are anti-aliasing. We store the
480 // bounds as floats to enable a faster quick reject implementation.
481 SkRect dst;
482 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
483 return dst;
484}
485
mtkleinfeaadee2015-04-08 11:25:48 -0700486void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
487 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700488 fMCRec->reset(bounds);
489
490 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500491 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400492 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700493 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700494 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700495}
496
Hal Canary363a3f82018-10-04 11:04:48 -0400497void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800498 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500501 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500502 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700503 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reeda499f902015-05-01 09:34:31 -0700505 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
506 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400507 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510
halcanary96fcdcc2015-08-27 07:41:13 -0700511 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000512
reedf92c8662014-08-18 08:02:43 -0700513 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700514 // The root device and the canvas should always have the same pixel geometry
515 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800516 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700517 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518
Mike Reedc42a1cd2017-02-14 14:25:14 -0500519 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700520 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400521
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500522 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
reed@google.comcde92112011-07-06 20:00:52 +0000525SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700527 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000528{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000529 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000530
Hal Canary363a3f82018-10-04 11:04:48 -0400531 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000532}
533
reed96a857e2015-01-25 10:33:58 -0800534SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800536 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537{
538 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400539 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500540 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700541}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000542
Hal Canary363a3f82018-10-04 11:04:48 -0400543SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700544 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700545 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700546{
547 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700548
Mike Reed566e53c2017-03-10 10:49:45 -0500549 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400550 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700551}
552
Herb Derbyefe39bc2018-05-01 17:06:20 -0400553SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700555 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000556{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700558
Hal Canary363a3f82018-10-04 11:04:48 -0400559 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700560}
561
reed4a8126e2014-09-22 07:29:03 -0700562SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700563 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700564 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700565{
566 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700567
Mike Reed910ca0f2018-04-25 13:04:05 -0400568 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400569 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700570}
reed29c857d2014-09-21 10:25:07 -0700571
Mike Reed356f7c22017-01-10 11:58:39 -0500572SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
573 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700574 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
575 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500576 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700577{
578 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700579
Mike Reed910ca0f2018-04-25 13:04:05 -0400580 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400581 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582}
583
Mike Reed356f7c22017-01-10 11:58:39 -0500584SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
585
Matt Sarett31f99ce2017-04-11 08:46:01 -0400586#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
587SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
588 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
589 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
590 , fAllocator(nullptr)
591{
592 inc_canvas();
593
594 SkBitmap tmp(bitmap);
595 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400596 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400597 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400598}
599#endif
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601SkCanvas::~SkCanvas() {
602 // free up the contents of our deque
603 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 this->internalRestore(); // restore the last, since we're going away
606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 dec_canvas();
608}
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610///////////////////////////////////////////////////////////////////////////////
611
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000612void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700613 this->onFlush();
614}
615
616void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000617 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000618 if (device) {
619 device->flush();
620 }
621}
622
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500623SkSurface* SkCanvas::getSurface() const {
624 return fSurfaceBase;
625}
626
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000627SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000628 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000629 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
630}
631
senorblancoafc7cce2016-02-02 18:44:15 -0800632SkIRect SkCanvas::getTopLayerBounds() const {
633 SkBaseDevice* d = this->getTopDevice();
634 if (!d) {
635 return SkIRect::MakeEmpty();
636 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500637 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800638}
639
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000640SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000642 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400644 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645}
646
Florin Malita0ed3b642017-01-13 16:56:38 +0000647SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400648 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000649}
650
Mike Reed353196f2017-07-21 11:01:18 -0400651bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000652 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400653 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000654}
655
Mike Reed353196f2017-07-21 11:01:18 -0400656bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
657 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400658}
659
660bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
661 SkPixmap pm;
662 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
663}
664
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000665bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400666 SkPixmap pm;
667 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700668 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000669 }
670 return false;
671}
672
Matt Sarett03dd6d52017-01-23 12:15:09 -0500673bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000675 SkBaseDevice* device = this->getDevice();
676 if (!device) {
677 return false;
678 }
679
Matt Sarett03dd6d52017-01-23 12:15:09 -0500680 // This check gives us an early out and prevents generation ID churn on the surface.
681 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
682 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400683 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500684 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000686
Matt Sarett03dd6d52017-01-23 12:15:09 -0500687 // Tell our owning surface to bump its generation ID.
688 const bool completeOverwrite =
689 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700690 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700691
Matt Sarett03dd6d52017-01-23 12:15:09 -0500692 // This can still fail, most notably in the case of a invalid color type or alpha type
693 // conversion. We could pull those checks into this function and avoid the unnecessary
694 // generation ID bump. But then we would be performing those checks twice, since they
695 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400696 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000697}
reed@google.com51df9e32010-12-23 19:29:18 +0000698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699//////////////////////////////////////////////////////////////////////////////
700
reed2ff1fce2014-12-11 07:07:37 -0800701void SkCanvas::checkForDeferredSave() {
702 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800703 this->doSave();
704 }
705}
706
reedf0090cb2014-11-26 08:55:51 -0800707int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800708#ifdef SK_DEBUG
709 int count = 0;
710 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
711 for (;;) {
712 const MCRec* rec = (const MCRec*)iter.next();
713 if (!rec) {
714 break;
715 }
716 count += 1 + rec->fDeferredSaveCount;
717 }
718 SkASSERT(count == fSaveCount);
719#endif
720 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800721}
722
723int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800724 fSaveCount += 1;
725 fMCRec->fDeferredSaveCount += 1;
726 return this->getSaveCount() - 1; // return our prev value
727}
728
729void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800730 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700731
732 SkASSERT(fMCRec->fDeferredSaveCount > 0);
733 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800734 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800735}
736
Mike Reedab7e8b32020-04-03 15:33:40 -0400737void SkCanvas::notifyCameraChanged() {
738 SkM44 invc; // start with identity
739 if (!fCameraStack.empty()) {
740 invc = fCameraStack.back().fInvPostCamera;
741 }
742 FOR_EACH_TOP_DEVICE(device->setInvCamera(invc));
743}
744
Mike Reed2f92c932020-04-06 15:44:17 -0400745// Don't trigger other virtuals from onSaveCamera
746void SkCanvas::onSaveCamera(const SkM44& projection, const SkM44& camera) {
747 (void)this->save();
748 this->internalConcat44(projection * camera);
749
Mike Reedb18e74d2020-01-16 13:58:22 -0500750 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedab7e8b32020-04-03 15:33:40 -0400751 this->notifyCameraChanged();
Mike Reed2f92c932020-04-06 15:44:17 -0400752}
Mike Reedab7e8b32020-04-03 15:33:40 -0400753
Mike Reed2f92c932020-04-06 15:44:17 -0400754int SkCanvas::saveCamera(const SkM44& projection, const SkM44& camera) {
755 this->onSaveCamera(projection, camera);
756 return this->getSaveCount();
Mike Reedee0a03a2020-01-14 16:44:47 -0500757}
758
reedf0090cb2014-11-26 08:55:51 -0800759void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800760 if (fMCRec->fDeferredSaveCount > 0) {
761 SkASSERT(fSaveCount > 1);
762 fSaveCount -= 1;
763 fMCRec->fDeferredSaveCount -= 1;
764 } else {
765 // check for underflow
766 if (fMCStack.count() > 1) {
767 this->willRestore();
768 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700769 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800770 this->internalRestore();
771 this->didRestore();
772 }
reedf0090cb2014-11-26 08:55:51 -0800773 }
774}
775
776void SkCanvas::restoreToCount(int count) {
777 // sanity check
778 if (count < 1) {
779 count = 1;
780 }
mtkleinf0f14112014-12-12 08:46:25 -0800781
reedf0090cb2014-11-26 08:55:51 -0800782 int n = this->getSaveCount() - count;
783 for (int i = 0; i < n; ++i) {
784 this->restore();
785 }
786}
787
reed2ff1fce2014-12-11 07:07:37 -0800788void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700790 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000792
Mike Reedc42a1cd2017-02-14 14:25:14 -0500793 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794}
795
reed4960eee2015-12-18 07:09:18 -0800796bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400797 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798}
799
reed4960eee2015-12-18 07:09:18 -0800800bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700801 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400802 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
803 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
804 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
805 // filter does not require a decomposed CTM matrix, the filter space and device space are the
806 // same. When it has been decomposed, we want the original image filter node to process the
807 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
808 // longer the original filter, but has the remainder matrix baked into it, and passing in the
809 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
810 // to the original filter node (barring inflation from consecutive calls to mapRect). While
811 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
812 // passing getDeviceClipBounds() to 'imageFilter' is correct.
813 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
814 // be important to more accurately calculate the clip bounds in the layer space for the original
815 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500816 SkIRect clipBounds = this->getDeviceClipBounds();
817 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000818 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000819 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000820
reed96e657d2015-03-10 17:30:07 -0700821 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
822
Robert Phillips12078432018-05-17 11:17:39 -0400823 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
824 // If the image filter DAG affects transparent black then we will need to render
825 // out to the clip bounds
826 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000827 }
Robert Phillips12078432018-05-17 11:17:39 -0400828
829 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700830 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700832 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400833 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400835 inputSaveLayerBounds = clipBounds;
836 }
837
838 if (imageFilter) {
839 // expand the clip bounds by the image filter DAG to include extra content that might
840 // be required by the image filters.
841 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
842 SkImageFilter::kReverse_MapDirection,
843 &inputSaveLayerBounds);
844 }
845
846 SkIRect clippedSaveLayerBounds;
847 if (bounds) {
848 // For better or for worse, user bounds currently act as a hard clip on the layer's
849 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
850 clippedSaveLayerBounds = inputSaveLayerBounds;
851 } else {
852 // If there are no user bounds, we don't want to artificially restrict the resulting
853 // layer bounds, so allow the expanded clip bounds free reign.
854 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800856
857 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400858 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800859 if (BoundsAffectsClip(saveLayerFlags)) {
860 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
861 fMCRec->fRasterClip.setEmpty();
862 fDeviceClipBounds.setEmpty();
863 }
864 return false;
865 }
Robert Phillips12078432018-05-17 11:17:39 -0400866 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867
reed4960eee2015-12-18 07:09:18 -0800868 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700869 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400870 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
871 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000872 }
873
874 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400875 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000876 }
Robert Phillips12078432018-05-17 11:17:39 -0400877
junov@chromium.orga907ac32012-02-24 21:54:07 +0000878 return true;
879}
880
reed4960eee2015-12-18 07:09:18 -0800881int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
882 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000883}
884
Cary Clarke041e312018-03-06 13:00:52 -0500885int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700886 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400887 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
888 // no need for the layer (or any of the draws until the matching restore()
889 this->save();
890 this->clipRect({0,0,0,0});
891 } else {
892 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
893 fSaveCount += 1;
894 this->internalSaveLayer(rec, strategy);
895 }
reed4960eee2015-12-18 07:09:18 -0800896 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800897}
898
Mike Reed148b7fd2018-12-18 17:38:18 -0500899int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
900 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
901 // Assuming clips never expand, if the request bounds is outside of the current clip
902 // there is no need to copy/restore the area, so just devolve back to a regular save.
903 this->save();
904 } else {
905 bool doTheWork = this->onDoSaveBehind(bounds);
906 fSaveCount += 1;
907 this->internalSave();
908 if (doTheWork) {
909 this->internalSaveBehind(bounds);
910 }
911 }
912 return this->getSaveCount() - 1;
913}
914
reeda2217ef2016-07-20 06:04:34 -0700915void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500916 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500917 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400918 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
919 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400920 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500921 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
922 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400923 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400924 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
925 // This means that we only have to copy a dst-sized block of pixels out of src and translate
926 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400927 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
928 dstOrigin.y() - src->getOrigin().y(),
929 dst->width(), dst->height());
930 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400931 return;
932 }
933
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400934 auto special = src->snapSpecial(snapBounds);
935 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400936 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
937 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400938 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
939 }
940 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400941 }
942
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400943 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
944 // by the backdrop filter.
945 SkMatrix toRoot, layerMatrix;
946 SkSize scale;
947 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
948 toRoot = SkMatrix::I();
949 layerMatrix = ctm;
950 } else if (ctm.decomposeScale(&scale, &toRoot)) {
951 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
952 } else {
953 // Perspective, for now, do no scaling of the layer itself.
954 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
955 // the matrix, e.g. based on the midpoint of the near/far planes?
956 toRoot = ctm;
957 layerMatrix = SkMatrix::I();
958 }
959
960 // We have to map the dst bounds from the root space into the layer space where filtering will
961 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
962 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
963 // is a safe, conservative estimate.
964 SkMatrix fromRoot;
965 if (!toRoot.invert(&fromRoot)) {
966 return;
967 }
968
969 // This represents what the backdrop filter needs to produce in the layer space, and is sized
970 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
971 SkIRect layerTargetBounds = fromRoot.mapRect(
972 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
973 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
974 // require some extra input pixels.
975 SkIRect layerInputBounds = filter->filterBounds(
976 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
977 &layerTargetBounds);
978
979 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400980 // be the conservative contents required to fill a layerInputBounds-sized surface with the
981 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400982 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
983 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
984 if (!backdropBounds.intersect(srcDevRect)) {
985 return;
986 }
987
988 auto special = src->snapSpecial(backdropBounds);
989 if (!special) {
990 return;
991 }
992
993 SkColorType colorType = src->imageInfo().colorType();
994 if (colorType == kUnknown_SkColorType) {
995 colorType = kRGBA_8888_SkColorType;
996 }
997 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400998
999 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001000 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001001 // Drawing the temporary and final filtered image requires a higher filter quality if the
1002 // 'toRoot' transformation is not identity, in order to minimize the impact on already
1003 // rendered edges/content.
1004 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
1005 p.setFilterQuality(kHigh_SkFilterQuality);
1006
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001007 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1008 // and stored in a temporary surface, which is then used as the input to the actual filter.
1009 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1010 if (!tmpSurface) {
1011 return;
1012 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001013
1014 auto tmpCanvas = tmpSurface->getCanvas();
1015 tmpCanvas->clear(SK_ColorTRANSPARENT);
1016 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1017 // space, then maps from root space into the layer space, then maps it so the input layer's
1018 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1019 // performed on backdropBounds.
1020 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1021 tmpCanvas->concat(fromRoot);
1022 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001023
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001024 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1025 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1026 special = tmpSurface->makeImageSnapshot();
1027 } else {
1028 // Since there is no extra transform that was done, update the input bounds to reflect
1029 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1030 // was equal to backdropBounds before it was made relative to the src device and cropped.
1031 // When we use the original snapped image directly, just map the update backdrop bounds
1032 // back into the shared layer space
1033 layerInputBounds = backdropBounds;
1034 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001035
1036 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1037 // draw will be 1-1 so there is no need to increase filter quality.
1038 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001039 }
1040
1041 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1042 // layer space. This has to further offset everything so that filter evaluation thinks the
1043 // source image's top left corner is (0, 0).
1044 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1045 // this can be simplified.
1046 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1047 SkMatrix filterCTM = layerMatrix;
1048 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1049 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1050
1051 SkIPoint offset;
1052 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001053 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001054 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1055 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1056 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1057 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001058 offset += layerInputBounds.topLeft();
1059
1060 // Manually setting the device's CTM requires accounting for the device's origin.
1061 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001062 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001063 // a global CTM instead of a device CTM.
1064 SkMatrix dstCTM = toRoot;
1065 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001066 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001067
1068 // And because devices don't have a special-image draw function that supports arbitrary
1069 // matrices, we are abusing the asImage() functionality here...
1070 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001071 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001072 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001073 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001074 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1075 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001076 }
robertphillips7354a4b2015-12-16 05:08:27 -08001077}
reed70ee31b2015-12-10 13:44:45 -08001078
Mike Kleine083f7c2018-02-07 12:54:27 -05001079static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001080 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001081 if (prev.bytesPerPixel() <= 4 &&
1082 prev.colorType() != kRGBA_8888_SkColorType &&
1083 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001084 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1085 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1086 ct = kN32_SkColorType;
1087 }
1088 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001089}
1090
reed4960eee2015-12-18 07:09:18 -08001091void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001092 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001093 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001094 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1095
Michael Ludwigeced98b2020-03-03 10:39:41 -05001096 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1097 // saveLayer ignores mask filters, so force it to null
1098 if (paint.get() && paint->getMaskFilter()) {
1099 paint.writable()->setMaskFilter(nullptr);
1100 }
1101
Mike Reed5532c2a2019-02-23 12:00:32 -05001102 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1103 // regardless of any hint-rect from the caller. skbug.com/8783
1104 if (rec.fBackdrop) {
1105 bounds = nullptr;
1106 }
1107
Michael Ludwigeced98b2020-03-03 10:39:41 -05001108 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001109 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001110 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001111
reed8c30a812016-04-20 16:36:51 -07001112 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001113 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1114 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1115 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001116 *
1117 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001118 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1119 * if necessary.
1120 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1121 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001122 * 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 -04001123 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001124 * of the original imagefilter, and draw that (via drawSprite)
1125 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1126 *
1127 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1128 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1129 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001130 if (imageFilter) {
1131 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001132 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1133 &modifiedCTM);
1134 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001135 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001136 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001137 modifiedRec = fMCRec;
1138 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001139 imageFilter = modifiedFilter.get();
1140 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001141 }
1142 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1143 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001144 }
reed8c30a812016-04-20 16:36:51 -07001145
junov@chromium.orga907ac32012-02-24 21:54:07 +00001146 // do this before we create the layer. We don't call the public save() since
1147 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001148 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001149
junov@chromium.orga907ac32012-02-24 21:54:07 +00001150 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001151 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001152 if (modifiedRec) {
1153 // In this case there will be no layer in which to stash the matrix so we need to
1154 // revert the prior MCRec to its earlier state.
1155 modifiedRec->fMatrix = stashedMatrix;
1156 }
reed2ff1fce2014-12-11 07:07:37 -08001157 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 }
1159
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001160 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1161 // the clipRectBounds() call above?
1162 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001163 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001164 }
1165
reed8dc0ccb2015-03-20 06:32:52 -07001166 SkPixelGeometry geo = fProps.pixelGeometry();
1167 if (paint) {
reed76033be2015-03-14 10:54:31 -07001168 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001169 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001170 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001171 }
1172 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173
robertphillips5139e502016-07-19 05:10:40 -07001174 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001175 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001176 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001177 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001178 }
reedb2db8982014-11-13 12:41:02 -08001179
Mike Kleine083f7c2018-02-07 12:54:27 -05001180 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001181 if (rec.fSaveLayerFlags & kF16ColorType) {
1182 info = info.makeColorType(kRGBA_F16_SkColorType);
1183 }
reed129ed1c2016-02-22 06:42:31 -08001184
Hal Canary704cd322016-11-07 14:13:52 -05001185 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001186 {
Florin Malita4571e492019-07-16 10:25:58 -04001187 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001188 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001189 const bool trackCoverage =
1190 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001191 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001192 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001193 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001194 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1195 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001196 return;
reed61f501f2015-04-29 08:34:00 -07001197 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001198 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001199 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200
Mike Reedb43a3e02017-02-11 10:18:58 -05001201 // only have a "next" if this new layer doesn't affect the clip (rare)
1202 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 fMCRec->fLayer = layer;
1204 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001205
Mike Reedc61abee2017-02-28 17:45:27 -05001206 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001207 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001208 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001209 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001210
Mike Reedc42a1cd2017-02-14 14:25:14 -05001211 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1212
1213 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1214 if (layer->fNext) {
1215 // need to punch a hole in the previous device, so we don't draw there, given that
1216 // the new top-layer will allow drawing to happen "below" it.
1217 SkRegion hole(ir);
1218 do {
1219 layer = layer->fNext;
1220 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1221 } while (layer->fNext);
1222 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223}
1224
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001225int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001226 if (0xFF == alpha) {
1227 return this->saveLayer(bounds, nullptr);
1228 } else {
1229 SkPaint tmpPaint;
1230 tmpPaint.setAlpha(alpha);
1231 return this->saveLayer(bounds, &tmpPaint);
1232 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001233}
1234
Mike Reed148b7fd2018-12-18 17:38:18 -05001235void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001236 SkBaseDevice* device = this->getTopDevice();
1237 if (nullptr == device) { // Do we still need this check???
1238 return;
1239 }
1240
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001241 // Map the local bounds into the top device's coordinate space (this is not
1242 // necessarily the full global CTM transform).
1243 SkIRect devBounds;
1244 if (localBounds) {
1245 SkRect tmp;
1246 device->localToDevice().mapRect(&tmp, *localBounds);
1247 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1248 devBounds.setEmpty();
1249 }
1250 } else {
1251 devBounds = device->devClipBounds();
1252 }
1253 if (devBounds.isEmpty()) {
1254 return;
1255 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001256
Michael Ludwigac352122019-08-28 21:03:05 +00001257 // This is getting the special image from the current device, which is then drawn into (both by
1258 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1259 // own device, we need to explicitly copy the back image contents so that its original content
1260 // is available when we splat it back later during restore.
1261 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001262 if (!backImage) {
1263 return;
1264 }
1265
1266 // we really need the save, so we can wack the fMCRec
1267 this->checkForDeferredSave();
1268
1269 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1270
1271 SkPaint paint;
1272 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001273 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001274}
1275
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276void SkCanvas::internalRestore() {
1277 SkASSERT(fMCStack.count() != 0);
1278
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001279 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 DeviceCM* layer = fMCRec->fLayer; // may be null
1281 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001282 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283
Mike Reed148b7fd2018-12-18 17:38:18 -05001284 // move this out before we do the actual restore
1285 auto backImage = std::move(fMCRec->fBackImage);
1286
Mike Reedb18e74d2020-01-16 13:58:22 -05001287 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1288 fCameraStack.pop_back();
Mike Reedab7e8b32020-04-03 15:33:40 -04001289 this->notifyCameraChanged();
Mike Reedb18e74d2020-01-16 13:58:22 -05001290 }
1291
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 // now do the normal restore()
1293 fMCRec->~MCRec(); // balanced in save()
1294 fMCStack.pop_back();
1295 fMCRec = (MCRec*)fMCStack.back();
1296
Mike Reedc42a1cd2017-02-14 14:25:14 -05001297 if (fMCRec) {
1298 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1299 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001300
Mike Reed148b7fd2018-12-18 17:38:18 -05001301 if (backImage) {
1302 SkPaint paint;
1303 paint.setBlendMode(SkBlendMode::kDstOver);
1304 const int x = backImage->fLoc.x();
1305 const int y = backImage->fLoc.y();
1306 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1307 nullptr, SkMatrix::I());
1308 }
1309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1311 since if we're being recorded, we don't want to record this (the
1312 recorder will have already recorded the restore).
1313 */
bsalomon49f085d2014-09-05 13:34:00 -07001314 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001315 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001316 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001317 // At this point, 'layer' has been removed from the device stack, so the devices that
1318 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1319 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001320 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001321 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001322 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001323 delete layer;
reedb679ca82015-04-07 04:40:48 -07001324 } else {
1325 // we're at the root
reeda499f902015-05-01 09:34:31 -07001326 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001327 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001328 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001330 }
msarettfbfa2582016-08-12 08:29:08 -07001331
1332 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001333 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001334 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1335 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336}
1337
reede8f30622016-03-23 18:59:25 -07001338sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001339 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001340 props = &fProps;
1341 }
1342 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001343}
1344
reede8f30622016-03-23 18:59:25 -07001345sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001346 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001347 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001348}
1349
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001350SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001351 return this->onImageInfo();
1352}
1353
1354SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001355 SkBaseDevice* dev = this->getDevice();
1356 if (dev) {
1357 return dev->imageInfo();
1358 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001359 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001360 }
1361}
1362
brianosman898235c2016-04-06 07:38:23 -07001363bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001364 return this->onGetProps(props);
1365}
1366
1367bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001368 SkBaseDevice* dev = this->getDevice();
1369 if (dev) {
1370 if (props) {
1371 *props = fProps;
1372 }
1373 return true;
1374 } else {
1375 return false;
1376 }
1377}
1378
reed6ceeebd2016-03-09 14:26:26 -08001379bool SkCanvas::peekPixels(SkPixmap* pmap) {
1380 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001381}
1382
reed884e97c2015-05-26 11:31:54 -07001383bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001384 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001385 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001386}
1387
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001388void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001389 SkPixmap pmap;
1390 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001391 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001392 }
1393 if (info) {
1394 *info = pmap.info();
1395 }
1396 if (rowBytes) {
1397 *rowBytes = pmap.rowBytes();
1398 }
1399 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001400 // If the caller requested the origin, they presumably are expecting the returned pixels to
1401 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1402 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1403 // instead of an origin, just don't expose the pixels in that case. Note that this means
1404 // that layers with complex coordinate spaces can still report their pixels if the caller
1405 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1406 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1407 *origin = this->getTopDevice()->getOrigin();
1408 } else {
1409 return nullptr;
1410 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001411 }
reed884e97c2015-05-26 11:31:54 -07001412 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001413}
1414
reed884e97c2015-05-26 11:31:54 -07001415bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001416 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001417 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001418}
1419
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421
Mike Reed8bcd1282019-03-13 16:51:54 -04001422// In our current design/features, we should never have a layer (src) in a different colorspace
1423// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1424// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1425// colorspace.
1426static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1427 SkASSERT(src == dst);
1428}
1429
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001430void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001431 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001433 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434 paint = &tmp;
1435 }
reed@google.com4b226022011-01-11 18:32:13 +00001436
Mike Reed38992392019-07-30 10:48:15 -04001437 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001438
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001440 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001441 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1442 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001443 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001444 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001445 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1446 // so it should always be possible to use the relative origin. Once drawDevice() and
1447 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1448 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001449 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001450 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1451 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001452 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1453 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001454 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1455 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001456 }
reed@google.com76dd2772012-01-05 21:15:07 +00001457 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001458 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 }
reeda2217ef2016-07-20 06:04:34 -07001461
Mike Reed38992392019-07-30 10:48:15 -04001462 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463}
1464
reed32704672015-12-16 08:27:10 -08001465/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001466
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001467void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001468 if (dx || dy) {
1469 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001470 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001471
reedfe69b502016-09-12 06:31:48 -07001472 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001473 // However, if either is non-finite, we might still complicate the matrix type,
1474 // so we still have to compute this.
1475 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001476
Mike Reedc42a1cd2017-02-14 14:25:14 -05001477 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001478
reedfe69b502016-09-12 06:31:48 -07001479 this->didTranslate(dx,dy);
1480 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481}
1482
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001483void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001484 if (sx != 1 || sy != 1) {
1485 this->checkForDeferredSave();
1486 fMCRec->fMatrix.preScale(sx, sy);
1487
1488 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1489 // but pre-scaling by a non-finite does change it, so we have to recompute.
1490 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1491
1492 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1493
1494 this->didScale(sx, sy);
1495 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496}
1497
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001498void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001499 SkMatrix m;
1500 m.setRotate(degrees);
1501 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502}
1503
bungeman7438bfc2016-07-12 15:01:19 -07001504void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1505 SkMatrix m;
1506 m.setRotate(degrees, px, py);
1507 this->concat(m);
1508}
1509
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001510void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001511 SkMatrix m;
1512 m.setSkew(sx, sy);
1513 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001514}
1515
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001516void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001517 if (matrix.isIdentity()) {
1518 return;
1519 }
1520
reed2ff1fce2014-12-11 07:07:37 -08001521 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001522 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001523
msarett9637ea92016-08-18 14:03:30 -07001524 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001525
Mike Reed7627fa52017-02-08 10:07:53 -05001526 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001527
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001528 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001529}
1530
Mike Reed2f92c932020-04-06 15:44:17 -04001531void SkCanvas::internalConcat44(const SkM44& m) {
Mike Reed403c8072020-01-08 10:40:39 -05001532 this->checkForDeferredSave();
1533
Mike Reed3ef77dd2020-04-06 10:41:09 -04001534 fMCRec->fMatrix.preConcat(m);
Mike Reed403c8072020-01-08 10:40:39 -05001535
1536 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1537
1538 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed2f92c932020-04-06 15:44:17 -04001539}
Mike Reed403c8072020-01-08 10:40:39 -05001540
Mike Reed2f92c932020-04-06 15:44:17 -04001541void SkCanvas::concat(const SkM44& m) {
1542 this->internalConcat44(m);
Mike Reeda735ad92020-04-06 21:32:43 -04001543 // notify subclasses
1544#ifdef SK_SUPPORT_LEGACY_DIDCONCAT44
1545 this->didConcat44(SkMatrixPriv::M44ColMajor(m));
1546#else
1547 this->didConcat44(m);
1548#endif
Mike Reedee3216d2020-01-17 17:35:04 -05001549}
1550
reed8c30a812016-04-20 16:36:51 -07001551void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001552 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001553 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001554
Mike Reedc42a1cd2017-02-14 14:25:14 -05001555 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001556}
1557
1558void SkCanvas::setMatrix(const SkMatrix& matrix) {
1559 this->checkForDeferredSave();
1560 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001561 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562}
1563
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001565 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566}
1567
1568//////////////////////////////////////////////////////////////////////////////
1569
Mike Reedc1f77742016-12-09 09:00:50 -05001570void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001571 if (!rect.isFinite()) {
1572 return;
1573 }
reed2ff1fce2014-12-11 07:07:37 -08001574 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001575 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1576 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001577}
1578
Mike Reedc1f77742016-12-09 09:00:50 -05001579void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001580 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001581
Mike Reed7627fa52017-02-08 10:07:53 -05001582 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001583
reedc64eff52015-11-21 12:39:45 -08001584 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001585 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1586 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001587 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588}
1589
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001590void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1591 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001592 if (fClipRestrictionRect.isEmpty()) {
1593 // we notify the device, but we *dont* resolve deferred saves (since we're just
1594 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001595 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001596 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001597 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001598 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001599 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001600 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001601 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1602 }
1603}
1604
Mike Reedc1f77742016-12-09 09:00:50 -05001605void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001606 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001607 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001608 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001609 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1610 } else {
1611 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001612 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001613}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001614
Mike Reedc1f77742016-12-09 09:00:50 -05001615void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001616 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001617
Brian Salomona3b45d42016-10-03 11:36:16 -04001618 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001619
Mike Reed7627fa52017-02-08 10:07:53 -05001620 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001621
Mike Reed20800c82017-11-15 16:09:04 -05001622 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1623 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001624 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001625}
1626
Mike Reedc1f77742016-12-09 09:00:50 -05001627void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001628 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001629 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001630
1631 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1632 SkRect r;
1633 if (path.isRect(&r)) {
1634 this->onClipRect(r, op, edgeStyle);
1635 return;
1636 }
1637 SkRRect rrect;
1638 if (path.isOval(&r)) {
1639 rrect.setOval(r);
1640 this->onClipRRect(rrect, op, edgeStyle);
1641 return;
1642 }
1643 if (path.isRRect(&rrect)) {
1644 this->onClipRRect(rrect, op, edgeStyle);
1645 return;
1646 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001647 }
robertphillips39f05382015-11-24 09:30:12 -08001648
1649 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001650}
1651
Mike Reedc1f77742016-12-09 09:00:50 -05001652void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001653 AutoValidateClip avc(this);
1654
Brian Salomona3b45d42016-10-03 11:36:16 -04001655 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001656
Mike Reed7627fa52017-02-08 10:07:53 -05001657 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658
Brian Salomona3b45d42016-10-03 11:36:16 -04001659 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001660 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001661 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001662 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663}
1664
Mike Reed121c2af2020-03-10 14:02:56 -04001665void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1666 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001667 if (sh->isOpaque()) {
1668 if (op == SkClipOp::kIntersect) {
1669 // we don't occlude anything, so skip this call
1670 } else {
1671 SkASSERT(op == SkClipOp::kDifference);
1672 // we occlude everything, so set the clip to empty
1673 this->clipRect({0,0,0,0});
1674 }
1675 } else {
1676 this->onClipShader(std::move(sh), op);
1677 }
Mike Reed121c2af2020-03-10 14:02:56 -04001678 }
1679}
1680
1681void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1682 AutoValidateClip avc(this);
1683
1684 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1685
1686 // we don't know how to mutate our conservative bounds, so we don't
1687}
1688
Mike Reedc1f77742016-12-09 09:00:50 -05001689void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001690 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001691 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001692}
1693
Mike Reedc1f77742016-12-09 09:00:50 -05001694void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001695 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001696
reed@google.com5c3d1472011-02-22 19:12:23 +00001697 AutoValidateClip avc(this);
1698
Mike Reed20800c82017-11-15 16:09:04 -05001699 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001700 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701}
1702
reed@google.com819c9212011-02-23 18:56:55 +00001703#ifdef SK_DEBUG
1704void SkCanvas::validateClip() const {
1705 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001706 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001707 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001708 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001709 return;
1710 }
reed@google.com819c9212011-02-23 18:56:55 +00001711}
1712#endif
1713
Mike Reeda1361362017-03-07 09:37:29 -05001714bool SkCanvas::androidFramework_isClipAA() const {
1715 bool containsAA = false;
1716
1717 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1718
1719 return containsAA;
1720}
1721
1722class RgnAccumulator {
1723 SkRegion* fRgn;
1724public:
1725 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1726 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1727 SkIPoint origin = device->getOrigin();
1728 if (origin.x() | origin.y()) {
1729 rgn->translate(origin.x(), origin.y());
1730 }
1731 fRgn->op(*rgn, SkRegion::kUnion_Op);
1732 }
1733};
1734
1735void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1736 RgnAccumulator accum(rgn);
1737 SkRegion tmp;
1738
1739 rgn->setEmpty();
1740 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001741}
1742
reed@google.com5c3d1472011-02-22 19:12:23 +00001743///////////////////////////////////////////////////////////////////////////////
1744
reed@google.com754de5f2014-02-24 19:38:20 +00001745bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001746 return fMCRec->fRasterClip.isEmpty();
1747
1748 // TODO: should we only use the conservative answer in a recording canvas?
1749#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001750 SkBaseDevice* dev = this->getTopDevice();
1751 // if no device we return true
1752 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001753#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001754}
1755
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001756bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001757 SkBaseDevice* dev = this->getTopDevice();
1758 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001759 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001760}
1761
msarettfbfa2582016-08-12 08:29:08 -07001762static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1763#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1764 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1765 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1766 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1767 return 0xF != _mm_movemask_ps(mask);
1768#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1769 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1770 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1771 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1772 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1773#else
1774 SkRect devRectAsRect;
1775 SkRect devClipAsRect;
1776 devRect.store(&devRectAsRect.fLeft);
1777 devClip.store(&devClipAsRect.fLeft);
1778 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1779#endif
1780}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001781
msarettfbfa2582016-08-12 08:29:08 -07001782// It's important for this function to not be inlined. Otherwise the compiler will share code
1783// between the fast path and the slow path, resulting in two slow paths.
1784static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1785 const SkMatrix& matrix) {
1786 SkRect deviceRect;
1787 matrix.mapRect(&deviceRect, src);
1788 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1789}
1790
1791bool SkCanvas::quickReject(const SkRect& src) const {
1792#ifdef SK_DEBUG
1793 // Verify that fDeviceClipBounds are set properly.
1794 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001795 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001796 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001797 } else {
msarettfbfa2582016-08-12 08:29:08 -07001798 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799 }
msarettfbfa2582016-08-12 08:29:08 -07001800
msarett9637ea92016-08-18 14:03:30 -07001801 // Verify that fIsScaleTranslate is set properly.
1802 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001803#endif
1804
msarett9637ea92016-08-18 14:03:30 -07001805 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001806 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1807 }
1808
1809 // We inline the implementation of mapScaleTranslate() for the fast path.
1810 float sx = fMCRec->fMatrix.getScaleX();
1811 float sy = fMCRec->fMatrix.getScaleY();
1812 float tx = fMCRec->fMatrix.getTranslateX();
1813 float ty = fMCRec->fMatrix.getTranslateY();
1814 Sk4f scale(sx, sy, sx, sy);
1815 Sk4f trans(tx, ty, tx, ty);
1816
1817 // Apply matrix.
1818 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1819
1820 // Make sure left < right, top < bottom.
1821 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1822 Sk4f min = Sk4f::Min(ltrb, rblt);
1823 Sk4f max = Sk4f::Max(ltrb, rblt);
1824 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1825 // ARM this sequence generates the fastest (a single instruction).
1826 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1827
1828 // Check if the device rect is NaN or outside the clip.
1829 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830}
1831
reed@google.com3b3e8952012-08-16 20:53:31 +00001832bool SkCanvas::quickReject(const SkPath& path) const {
1833 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834}
1835
Mike Klein83c8dd92017-11-28 17:08:45 -05001836SkRect SkCanvas::getLocalClipBounds() const {
1837 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001838 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001839 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840 }
1841
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001842 SkMatrix inverse;
1843 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001844 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001845 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001846 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847
Mike Reed42e8c532017-01-23 14:09:13 -05001848 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001849 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001850 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001851
Mike Reedb57b9312018-04-23 12:12:54 -04001852 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001853 inverse.mapRect(&bounds, r);
1854 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855}
1856
Mike Klein83c8dd92017-11-28 17:08:45 -05001857SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001858 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001859}
1860
Mike Reedb18e74d2020-01-16 13:58:22 -05001861///////////////////////////////////////////////////////////////////////
1862
1863SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1864 : fMCRec(owner)
1865 , fCamera(camera)
1866{
1867 // assumes the mcrec has already been concatenated with the camera
1868 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1869 fInvPostCamera.setIdentity();
1870 }
1871}
1872
Mike Reed403c8072020-01-08 10:40:39 -05001873SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001874 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875}
1876
Mike Reed46f5c5f2020-02-20 15:42:29 -05001877SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001878 return fMCRec->fMatrix;
1879}
1880
Mike Reedc43f2a02020-01-16 14:54:34 -05001881SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001882 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001883 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001884 } else {
1885 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001886 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001887 }
1888}
1889
Mike Reedc43f2a02020-01-16 14:54:34 -05001890SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001891 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001892 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001893 } else {
1894 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001895 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001896 }
1897}
1898
Brian Osman11052242016-10-27 14:47:55 -04001899GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001900 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001901 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001902}
1903
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001904GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001905 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001906 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001907}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001908
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001909void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1910 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001911 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001912 if (outer.isEmpty()) {
1913 return;
1914 }
1915 if (inner.isEmpty()) {
1916 this->drawRRect(outer, paint);
1917 return;
1918 }
1919
1920 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001921 // be able to return ...
1922 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001923 //
1924 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001925 if (!outer.getBounds().contains(inner.getBounds())) {
1926 return;
1927 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001928
1929 this->onDrawDRRect(outer, inner, paint);
1930}
1931
reed41af9662015-01-05 07:49:08 -08001932void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001933 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001934 this->onDrawPaint(paint);
1935}
1936
1937void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001938 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001939 // To avoid redundant logic in our culling code and various backends, we always sort rects
1940 // before passing them along.
1941 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001942}
1943
Mike Reedd5674082019-04-19 15:00:47 -04001944void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1945 TRACE_EVENT0("skia", TRACE_FUNC);
1946 this->onDrawBehind(paint);
1947}
1948
msarettdca352e2016-08-26 06:37:45 -07001949void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001950 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001951 if (region.isEmpty()) {
1952 return;
1953 }
1954
1955 if (region.isRect()) {
1956 return this->drawIRect(region.getBounds(), paint);
1957 }
1958
1959 this->onDrawRegion(region, paint);
1960}
1961
reed41af9662015-01-05 07:49:08 -08001962void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001963 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001964 // To avoid redundant logic in our culling code and various backends, we always sort rects
1965 // before passing them along.
1966 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001967}
1968
1969void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001970 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001971 this->onDrawRRect(rrect, paint);
1972}
1973
1974void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001975 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001976 this->onDrawPoints(mode, count, pts, paint);
1977}
1978
Mike Reede88a1cb2017-03-17 09:50:46 -04001979void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1980 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001981 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001982}
1983
1984void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001985 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001986 RETURN_ON_NULL(vertices);
Brian Osmanf11e3312020-03-24 14:57:38 -04001987
Mike Reed5caf9352020-03-02 14:57:09 -05001988 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Brian Osman8cbedf92020-03-31 10:38:31 -04001989 SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);
Brian Osmanf11e3312020-03-24 14:57:38 -04001990
1991 // If the vertices contain custom attributes, ensure they line up with the paint's shader
1992 const SkRuntimeEffect* effect =
1993 paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001994 if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) {
Brian Osmanf11e3312020-03-24 14:57:38 -04001995 return;
1996 }
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001997 if (effect) {
1998 int attrIndex = 0;
1999 for (const auto& v : effect->varyings()) {
2000 if (vertices->priv().attributes()[attrIndex++].channelCount() != v.fWidth) {
2001 return;
2002 }
2003 }
2004 }
Brian Osmanf11e3312020-03-24 14:57:38 -04002005
Mike Reed5caf9352020-03-02 14:57:09 -05002006 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08002007}
2008
2009void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002010 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08002011 this->onDrawPath(path, paint);
2012}
2013
reeda85d4d02015-05-06 12:56:48 -07002014void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002015 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002016 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07002017 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08002018}
2019
Mike Reedc4e31092018-01-30 11:15:27 -05002020// Returns true if the rect can be "filled" : non-empty and finite
2021static bool fillable(const SkRect& r) {
2022 SkScalar w = r.width();
2023 SkScalar h = r.height();
2024 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
2025}
2026
reede47829b2015-08-06 10:02:53 -07002027void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2028 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002029 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002030 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002031 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002032 return;
2033 }
2034 this->onDrawImageRect(image, &src, dst, paint, constraint);
2035}
reed41af9662015-01-05 07:49:08 -08002036
reed84984ef2015-07-17 07:09:43 -07002037void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2038 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002039 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002040 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002041}
2042
Brian Salomonf08002c2018-10-26 16:15:46 -04002043void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002044 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002045 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002046 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002047}
reede47829b2015-08-06 10:02:53 -07002048
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002049namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002050class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002051public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002052 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2053 if (!origPaint) {
2054 return;
2055 }
2056 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2057 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2058 }
2059 if (origPaint->getMaskFilter()) {
2060 fPaint.writable()->setMaskFilter(nullptr);
2061 }
2062 if (origPaint->isAntiAlias()) {
2063 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002064 }
2065 }
2066
2067 const SkPaint* get() const {
2068 return fPaint;
2069 }
2070
2071private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002072 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002073};
2074} // namespace
2075
reed4c21dc52015-06-25 12:32:03 -07002076void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2077 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002078 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002079 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002080 if (dst.isEmpty()) {
2081 return;
2082 }
msarett552bca92016-08-03 06:53:26 -07002083 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002084 LatticePaint latticePaint(paint);
2085 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002086 } else {
reede47829b2015-08-06 10:02:53 -07002087 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002088 }
reed4c21dc52015-06-25 12:32:03 -07002089}
2090
msarett16882062016-08-16 09:31:08 -07002091void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2092 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002093 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002094 RETURN_ON_NULL(image);
2095 if (dst.isEmpty()) {
2096 return;
2097 }
msarett71df2d72016-09-30 12:41:42 -07002098
2099 SkIRect bounds;
2100 Lattice latticePlusBounds = lattice;
2101 if (!latticePlusBounds.fBounds) {
2102 bounds = SkIRect::MakeWH(image->width(), image->height());
2103 latticePlusBounds.fBounds = &bounds;
2104 }
2105
2106 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002107 LatticePaint latticePaint(paint);
2108 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002109 } else {
2110 this->drawImageRect(image, dst, paint);
2111 }
2112}
2113
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002114static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002115 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002116 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002117 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002118 return SkImage::MakeFromBitmap(bitmap);
2119}
2120
2121void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2122 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002123}
2124
reede47829b2015-08-06 10:02:53 -07002125void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002126 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002127 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002128}
2129
reed84984ef2015-07-17 07:09:43 -07002130void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2131 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002132 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002133}
2134
reede47829b2015-08-06 10:02:53 -07002135void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2136 SrcRectConstraint constraint) {
2137 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2138 constraint);
2139}
reede47829b2015-08-06 10:02:53 -07002140
reed71c3c762015-06-24 10:29:17 -07002141void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002142 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002143 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002144 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002145 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002146 if (count <= 0) {
2147 return;
2148 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002149 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002150 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002151 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002152}
2153
reedf70b5312016-03-04 16:36:20 -08002154void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002155 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002156 if (key) {
2157 this->onDrawAnnotation(rect, key, value);
2158 }
2159}
2160
reede47829b2015-08-06 10:02:53 -07002161void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2162 const SkPaint* paint, SrcRectConstraint constraint) {
2163 if (src) {
2164 this->drawImageRect(image, *src, dst, paint, constraint);
2165 } else {
2166 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2167 dst, paint, constraint);
2168 }
2169}
reede47829b2015-08-06 10:02:53 -07002170
Mike Reed4204da22017-05-17 08:53:36 -04002171void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002172 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002173 this->onDrawShadowRec(path, rec);
2174}
2175
2176void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2177 SkPaint paint;
2178 const SkRect& pathBounds = path.getBounds();
2179
Mike Reed38992392019-07-30 10:48:15 -04002180 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002181 while (iter.next()) {
2182 iter.fDevice->drawShadow(path, rec);
2183 }
Mike Reed38992392019-07-30 10:48:15 -04002184 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002185}
2186
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002187void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002188 QuadAAFlags aaFlags, const SkColor4f& color,
2189 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002190 TRACE_EVENT0("skia", TRACE_FUNC);
2191 // Make sure the rect is sorted before passing it along
2192 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2193}
2194
2195void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2196 const SkPoint dstClips[],
2197 const SkMatrix preViewMatrices[],
2198 const SkPaint* paint,
2199 SrcRectConstraint constraint) {
2200 TRACE_EVENT0("skia", TRACE_FUNC);
2201 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2202}
2203
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204//////////////////////////////////////////////////////////////////////////////
2205// These are the virtual drawing methods
2206//////////////////////////////////////////////////////////////////////////////
2207
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002208void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002209 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002210 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2211 }
2212}
2213
reed41af9662015-01-05 07:49:08 -08002214void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002215 this->internalDrawPaint(paint);
2216}
2217
2218void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002219 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220
2221 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002222 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002223 }
2224
Mike Reed38992392019-07-30 10:48:15 -04002225 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226}
2227
reed41af9662015-01-05 07:49:08 -08002228void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2229 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230 if ((long)count <= 0) {
2231 return;
2232 }
2233
Mike Reed822128b2017-02-28 16:41:03 -05002234 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002235 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002236 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002237 // special-case 2 points (common for drawing a single line)
2238 if (2 == count) {
2239 r.set(pts[0], pts[1]);
2240 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002241 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002242 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002243 if (!r.isFinite()) {
2244 return;
2245 }
Mike Reed822128b2017-02-28 16:41:03 -05002246 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002247 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2248 return;
2249 }
2250 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002251 }
reed@google.coma584aed2012-05-16 14:06:02 +00002252
halcanary96fcdcc2015-08-27 07:41:13 -07002253 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254
Mike Reed38992392019-07-30 10:48:15 -04002255 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002256
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002258 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259 }
reed@google.com4b226022011-01-11 18:32:13 +00002260
Mike Reed38992392019-07-30 10:48:15 -04002261 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262}
2263
reed4a167172016-08-18 17:15:25 -07002264static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002265 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002266}
2267
reed41af9662015-01-05 07:49:08 -08002268void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002269 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002271 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002272 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002273 return;
2274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 }
reed@google.com4b226022011-01-11 18:32:13 +00002276
reed4a167172016-08-18 17:15:25 -07002277 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002278 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279
reed4a167172016-08-18 17:15:25 -07002280 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002281 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002282 }
2283
Mike Reed38992392019-07-30 10:48:15 -04002284 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002285 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002286 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002287 SkDrawIter iter(this);
2288 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002289 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002290 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002291 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292}
2293
msarett44df6512016-08-25 13:54:30 -07002294void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002295 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002296 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002297 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002298 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2299 return;
2300 }
msarett44df6512016-08-25 13:54:30 -07002301 }
2302
Mike Reed38992392019-07-30 10:48:15 -04002303 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002304
2305 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002306 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002307 }
2308
Mike Reed38992392019-07-30 10:48:15 -04002309 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002310}
2311
Mike Reedd5674082019-04-19 15:00:47 -04002312void SkCanvas::onDrawBehind(const SkPaint& paint) {
2313 SkIRect bounds;
2314 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2315 for (;;) {
2316 const MCRec* rec = (const MCRec*)iter.prev();
2317 if (!rec) {
2318 return; // no backimages, so nothing to draw
2319 }
2320 if (rec->fBackImage) {
2321 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2322 rec->fBackImage->fImage->width(),
2323 rec->fBackImage->fImage->height());
2324 break;
2325 }
2326 }
2327
Mike Reed38992392019-07-30 10:48:15 -04002328 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002329
2330 while (iter.next()) {
2331 SkBaseDevice* dev = iter.fDevice;
2332
Mike Reedd5674082019-04-19 15:00:47 -04002333 dev->save();
2334 // We use clipRegion because it is already defined to operate in dev-space
2335 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2336 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002337 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002338 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002339 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002340 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002341 }
2342
Mike Reed38992392019-07-30 10:48:15 -04002343 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002344}
2345
reed41af9662015-01-05 07:49:08 -08002346void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002347 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002348 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002349 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002350 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002351 return;
2352 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002353 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002354
Mike Reed38992392019-07-30 10:48:15 -04002355 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002356
2357 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002358 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002359 }
2360
Mike Reed38992392019-07-30 10:48:15 -04002361 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002362}
2363
bsalomonac3aa242016-08-19 11:25:19 -07002364void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2365 SkScalar sweepAngle, bool useCenter,
2366 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002367 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002368 if (paint.canComputeFastBounds()) {
2369 SkRect storage;
2370 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002371 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002372 return;
2373 }
bsalomonac3aa242016-08-19 11:25:19 -07002374 }
2375
Mike Reed38992392019-07-30 10:48:15 -04002376 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002377
2378 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002379 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002380 }
2381
Mike Reed38992392019-07-30 10:48:15 -04002382 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002383}
2384
reed41af9662015-01-05 07:49:08 -08002385void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002386 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002387 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002388 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2389 return;
2390 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002391 }
2392
2393 if (rrect.isRect()) {
2394 // call the non-virtual version
2395 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002396 return;
2397 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002398 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002399 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2400 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002401 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002402
Mike Reed38992392019-07-30 10:48:15 -04002403 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002404
2405 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002406 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002407 }
2408
Mike Reed38992392019-07-30 10:48:15 -04002409 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002410}
2411
Mike Reed822128b2017-02-28 16:41:03 -05002412void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002413 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002414 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002415 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2416 return;
2417 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002418 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002419
Mike Reed38992392019-07-30 10:48:15 -04002420 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002421
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002422 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002423 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002424 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002425
Mike Reed38992392019-07-30 10:48:15 -04002426 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002427}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002428
reed41af9662015-01-05 07:49:08 -08002429void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002430 if (!path.isFinite()) {
2431 return;
2432 }
2433
Mike Reed822128b2017-02-28 16:41:03 -05002434 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002435 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002436 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002437 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2438 return;
2439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002441
Mike Reed822128b2017-02-28 16:41:03 -05002442 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002443 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002444 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002445 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002446 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002447 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448
Mike Reed38992392019-07-30 10:48:15 -04002449 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450
2451 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002452 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453 }
2454
Mike Reed38992392019-07-30 10:48:15 -04002455 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002456}
2457
reed262a71b2015-12-05 13:07:27 -08002458bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002459 if (!paint.getImageFilter()) {
2460 return false;
2461 }
2462
2463 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002464 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002465 return false;
2466 }
2467
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002468 // The other paint effects need to be applied before the image filter, but the sprite draw
2469 // applies the filter explicitly first.
2470 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2471 return false;
2472 }
reed262a71b2015-12-05 13:07:27 -08002473 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2474 // Once we can filter and the filter will return a result larger than itself, we should be
2475 // able to remove this constraint.
2476 // skbug.com/4526
2477 //
2478 SkPoint pt;
2479 ctm.mapXY(x, y, &pt);
2480 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2481 return ir.contains(fMCRec->fRasterClip.getBounds());
2482}
2483
Mike Reedf441cfc2018-04-11 14:50:16 -04002484// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2485// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2486// null.
2487static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2488 if (paintParam) {
2489 *real = *paintParam;
2490 real->setStyle(SkPaint::kFill_Style);
2491 real->setPathEffect(nullptr);
2492 paintParam = real;
2493 }
2494 return paintParam;
2495}
2496
reeda85d4d02015-05-06 12:56:48 -07002497void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002498 SkPaint realPaint;
2499 paint = init_image_paint(&realPaint, paint);
2500
reeda85d4d02015-05-06 12:56:48 -07002501 SkRect bounds = SkRect::MakeXYWH(x, y,
2502 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002503 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002504 SkRect tmp = bounds;
2505 if (paint) {
2506 paint->computeFastBounds(tmp, &tmp);
2507 }
2508 if (this->quickReject(tmp)) {
2509 return;
2510 }
reeda85d4d02015-05-06 12:56:48 -07002511 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002512 // At this point we need a real paint object. If the caller passed null, then we should
2513 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2514 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2515 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002516
reeda2217ef2016-07-20 06:04:34 -07002517 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002518 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2519 *paint);
2520 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002521 special = this->getDevice()->makeSpecial(image);
2522 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002523 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002524 }
2525 }
2526
Mike Reed38992392019-07-30 10:48:15 -04002527 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002528
reeda85d4d02015-05-06 12:56:48 -07002529 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002530 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002531 if (special) {
2532 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002533 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002534 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002535 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002536 SkScalarRoundToInt(pt.fY), pnt,
2537 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002538 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002539 iter.fDevice->drawImageRect(
2540 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2541 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002542 }
reeda85d4d02015-05-06 12:56:48 -07002543 }
halcanary9d524f22016-03-29 09:03:52 -07002544
Mike Reed38992392019-07-30 10:48:15 -04002545 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002546}
2547
reed41af9662015-01-05 07:49:08 -08002548void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002549 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002550 SkPaint realPaint;
2551 paint = init_image_paint(&realPaint, paint);
2552
halcanary96fcdcc2015-08-27 07:41:13 -07002553 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002554 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002555 if (paint) {
2556 paint->computeFastBounds(dst, &storage);
2557 }
2558 if (this->quickReject(storage)) {
2559 return;
2560 }
reeda85d4d02015-05-06 12:56:48 -07002561 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002562 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002563
Mike Reed38992392019-07-30 10:48:15 -04002564 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002565
reeda85d4d02015-05-06 12:56:48 -07002566 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002567 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002568 }
halcanary9d524f22016-03-29 09:03:52 -07002569
Mike Reed38992392019-07-30 10:48:15 -04002570 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002571}
2572
reed4c21dc52015-06-25 12:32:03 -07002573void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2574 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002575 SkPaint realPaint;
2576 paint = init_image_paint(&realPaint, paint);
2577
halcanary96fcdcc2015-08-27 07:41:13 -07002578 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002579 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002580 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2581 return;
2582 }
reed@google.com3d608122011-11-21 15:16:16 +00002583 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002584 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002585
Mike Reed38992392019-07-30 10:48:15 -04002586 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002587
reed4c21dc52015-06-25 12:32:03 -07002588 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002589 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002590 }
halcanary9d524f22016-03-29 09:03:52 -07002591
Mike Reed38992392019-07-30 10:48:15 -04002592 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002593}
2594
msarett16882062016-08-16 09:31:08 -07002595void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2596 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002597 SkPaint realPaint;
2598 paint = init_image_paint(&realPaint, paint);
2599
msarett16882062016-08-16 09:31:08 -07002600 if (nullptr == paint || paint->canComputeFastBounds()) {
2601 SkRect storage;
2602 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2603 return;
2604 }
2605 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002606 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002607
Mike Reed38992392019-07-30 10:48:15 -04002608 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002609
2610 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002611 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002612 }
2613
Mike Reed38992392019-07-30 10:48:15 -04002614 DRAW_END
msarett16882062016-08-16 09:31:08 -07002615}
2616
fmalita00d5c2c2014-08-21 08:53:26 -07002617void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2618 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002619 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002620 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002621 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002622 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002623 SkRect tmp;
2624 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2625 return;
2626 }
2627 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002628 }
2629
fmalita024f9962015-03-03 19:08:17 -08002630 // We cannot filter in the looper as we normally do, because the paint is
2631 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002632 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002633
fmalitaaa1b9122014-08-28 14:32:24 -07002634 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002635 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002636 }
2637
Mike Reed38992392019-07-30 10:48:15 -04002638 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002639}
2640
Mike Reed358fcad2018-11-23 15:27:51 -05002641// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002642void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002643 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2644 TRACE_EVENT0("skia", TRACE_FUNC);
2645 if (byteLength) {
2646 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002647 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002648 }
2649}
Mike Reed4f81bb72019-01-23 09:23:00 -05002650
fmalita00d5c2c2014-08-21 08:53:26 -07002651void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2652 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002653 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002654 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002655 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002656 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002657}
reed@google.come0d9ce82014-04-23 04:00:17 +00002658
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002659void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2660 const SkPaint& paint) {
2661 DRAW_BEGIN(paint, nullptr)
2662
2663 while (iter.next()) {
2664 // In the common case of one iteration we could std::move vertices here.
2665 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2666 }
2667
2668 DRAW_END
2669}
Brian Salomon199fb872017-02-06 09:41:10 -05002670
dandovb3c9d1c2014-08-12 08:34:29 -07002671void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002672 const SkPoint texCoords[4], SkBlendMode bmode,
2673 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002674 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002675 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002676 return;
2677 }
mtklein6cfa73a2014-08-13 13:33:49 -07002678
Mike Reedfaba3712016-11-03 14:45:31 -04002679 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002680}
2681
2682void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002683 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002684 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002685 // Since a patch is always within the convex hull of the control points, we discard it when its
2686 // bounding rectangle is completely outside the current clip.
2687 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002688 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002689 if (this->quickReject(bounds)) {
2690 return;
2691 }
mtklein6cfa73a2014-08-13 13:33:49 -07002692
Mike Reed38992392019-07-30 10:48:15 -04002693 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002694
dandovecfff212014-08-04 10:02:00 -07002695 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002696 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002697 }
mtklein6cfa73a2014-08-13 13:33:49 -07002698
Mike Reed38992392019-07-30 10:48:15 -04002699 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002700}
2701
reeda8db7282015-07-07 10:22:31 -07002702void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002703#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002704 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002705#endif
reede3b38ce2016-01-08 09:18:44 -08002706 RETURN_ON_NULL(dr);
2707 if (x || y) {
2708 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2709 this->onDrawDrawable(dr, &matrix);
2710 } else {
2711 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002712 }
2713}
2714
reeda8db7282015-07-07 10:22:31 -07002715void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002716#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002717 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002718#endif
reede3b38ce2016-01-08 09:18:44 -08002719 RETURN_ON_NULL(dr);
2720 if (matrix && matrix->isIdentity()) {
2721 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002722 }
reede3b38ce2016-01-08 09:18:44 -08002723 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002724}
2725
2726void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002727 // drawable bounds are no longer reliable (e.g. android displaylist)
2728 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002729 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002730}
2731
reed71c3c762015-06-24 10:29:17 -07002732void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002733 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002734 const SkRect* cull, const SkPaint* paint) {
2735 if (cull && this->quickReject(*cull)) {
2736 return;
2737 }
2738
2739 SkPaint pnt;
2740 if (paint) {
2741 pnt = *paint;
2742 }
halcanary9d524f22016-03-29 09:03:52 -07002743
Mike Reed38992392019-07-30 10:48:15 -04002744 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002745 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002746 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002747 }
Mike Reed38992392019-07-30 10:48:15 -04002748 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002749}
2750
reedf70b5312016-03-04 16:36:20 -08002751void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2752 SkASSERT(key);
2753
2754 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002755 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002756 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002757 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002758 }
Mike Reed38992392019-07-30 10:48:15 -04002759 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002760}
2761
Michael Ludwiga595f862019-08-27 15:25:49 -04002762void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2763 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002764 SkASSERT(r.isSorted());
2765
2766 // If this used a paint, it would be a filled color with blend mode, which does not
2767 // need to use an autodraw loop, so use SkDrawIter directly.
2768 if (this->quickReject(r)) {
2769 return;
2770 }
2771
Michael Ludwiga4b44882019-08-28 14:34:58 -04002772 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002773 SkDrawIter iter(this);
2774 while(iter.next()) {
2775 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2776 }
2777}
2778
2779void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2780 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2781 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002782 if (count <= 0) {
2783 // Nothing to draw
2784 return;
2785 }
2786
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002787 SkPaint realPaint;
2788 init_image_paint(&realPaint, paint);
2789
Michael Ludwiga4b44882019-08-28 14:34:58 -04002790 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2791 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2792 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2793 // or we need it for the autolooper (since it greatly improves image filter perf).
2794 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2795 bool setBoundsValid = count == 1 || needsAutoLooper;
2796 SkRect setBounds = imageSet[0].fDstRect;
2797 if (imageSet[0].fMatrixIndex >= 0) {
2798 // Account for the per-entry transform that is applied prior to the CTM when drawing
2799 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002800 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002801 if (needsAutoLooper) {
2802 for (int i = 1; i < count; ++i) {
2803 SkRect entryBounds = imageSet[i].fDstRect;
2804 if (imageSet[i].fMatrixIndex >= 0) {
2805 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2806 }
2807 setBounds.joinPossiblyEmptyRect(entryBounds);
2808 }
2809 }
2810
2811 // If we happen to have the draw bounds, though, might as well check quickReject().
2812 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2813 SkRect tmp;
2814 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2815 return;
2816 }
2817 }
2818
2819 if (needsAutoLooper) {
2820 SkASSERT(setBoundsValid);
2821 DRAW_BEGIN(realPaint, &setBounds)
2822 while (iter.next()) {
2823 iter.fDevice->drawEdgeAAImageSet(
2824 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2825 }
2826 DRAW_END
2827 } else {
2828 this->predrawNotify();
2829 SkDrawIter iter(this);
2830 while(iter.next()) {
2831 iter.fDevice->drawEdgeAAImageSet(
2832 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2833 }
2834 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002835}
2836
reed@android.com8a1c16f2008-12-17 15:59:43 +00002837//////////////////////////////////////////////////////////////////////////////
2838// These methods are NOT virtual, and therefore must call back into virtual
2839// methods, rather than actually drawing themselves.
2840//////////////////////////////////////////////////////////////////////////////
2841
reed374772b2016-10-05 17:33:02 -07002842void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002843 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002844 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002845 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002846 this->drawPaint(paint);
2847}
2848
2849void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002850 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002851 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2852}
2853
Mike Reed3661bc92017-02-22 13:21:42 -05002854void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002855 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002856 pts[0].set(x0, y0);
2857 pts[1].set(x1, y1);
2858 this->drawPoints(kLines_PointMode, 2, pts, paint);
2859}
2860
Mike Reed3661bc92017-02-22 13:21:42 -05002861void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 if (radius < 0) {
2863 radius = 0;
2864 }
2865
2866 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002867 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002868 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002869}
2870
2871void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2872 const SkPaint& paint) {
2873 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002874 SkRRect rrect;
2875 rrect.setRectXY(r, rx, ry);
2876 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002877 } else {
2878 this->drawRect(r, paint);
2879 }
2880}
2881
reed@android.com8a1c16f2008-12-17 15:59:43 +00002882void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2883 SkScalar sweepAngle, bool useCenter,
2884 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002885 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002886 if (oval.isEmpty() || !sweepAngle) {
2887 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888 }
bsalomon21af9ca2016-08-25 12:29:23 -07002889 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002890}
2891
reed@android.comf76bacf2009-05-13 14:00:33 +00002892///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002893#ifdef SK_DISABLE_SKPICTURE
2894void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002895
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002896
2897void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2898 const SkPaint* paint) {}
2899#else
Mike Klein88d90712018-01-27 17:30:04 +00002900/**
2901 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2902 * against the playback cost of recursing into the subpicture to get at its actual ops.
2903 *
2904 * For now we pick a conservatively small value, though measurement (and other heuristics like
2905 * the type of ops contained) may justify changing this value.
2906 */
2907#define kMaxPictureOpsToUnrollInsteadOfRef 1
2908
reedd5fa1a42014-08-09 11:08:05 -07002909void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002910 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002911 RETURN_ON_NULL(picture);
2912
reede3b38ce2016-01-08 09:18:44 -08002913 if (matrix && matrix->isIdentity()) {
2914 matrix = nullptr;
2915 }
Mike Klein88d90712018-01-27 17:30:04 +00002916 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2917 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2918 picture->playback(this);
2919 } else {
2920 this->onDrawPicture(picture, matrix, paint);
2921 }
reedd5fa1a42014-08-09 11:08:05 -07002922}
robertphillips9b14f262014-06-04 05:40:44 -07002923
reedd5fa1a42014-08-09 11:08:05 -07002924void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2925 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002926 if (!paint || paint->canComputeFastBounds()) {
2927 SkRect bounds = picture->cullRect();
2928 if (paint) {
2929 paint->computeFastBounds(bounds, &bounds);
2930 }
2931 if (matrix) {
2932 matrix->mapRect(&bounds);
2933 }
2934 if (this->quickReject(bounds)) {
2935 return;
2936 }
2937 }
2938
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002939 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002940 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002941}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002942#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002943
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944///////////////////////////////////////////////////////////////////////////////
2945///////////////////////////////////////////////////////////////////////////////
2946
reed3aafe112016-08-18 12:45:34 -07002947SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002948 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949
2950 SkASSERT(canvas);
2951
reed3aafe112016-08-18 12:45:34 -07002952 fImpl = new (fStorage) SkDrawIter(canvas);
Michael Ludwig2a8a3ff2020-03-26 15:33:20 -04002953 // This advances the base iterator to the first device and caches its origin,
2954 // correctly handling the case where there are no devices.
2955 this->next();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002956}
2957
2958SkCanvas::LayerIter::~LayerIter() {
2959 fImpl->~SkDrawIter();
2960}
2961
2962void SkCanvas::LayerIter::next() {
2963 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002964 if (!fDone) {
2965 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2966 // filters, so its devices will always be able to report the origin exactly.
2967 fDeviceOrigin = fImpl->fDevice->getOrigin();
2968 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002969}
2970
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002971SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002972 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002973}
2974
2975const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002976 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977}
2978
2979const SkPaint& SkCanvas::LayerIter::paint() const {
2980 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002981 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002982 paint = &fDefaultPaint;
2983 }
2984 return *paint;
2985}
2986
Mike Reedca37f322018-03-08 13:22:16 -05002987SkIRect SkCanvas::LayerIter::clipBounds() const {
2988 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002989}
2990
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002991int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2992int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002993
2994///////////////////////////////////////////////////////////////////////////////
2995
Brian Osmane8a98632019-04-10 10:26:10 -04002996SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2997SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2998SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2999SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3000
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003001SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3002 const SkRect& dstRect, int matrixIndex, float alpha,
3003 unsigned aaFlags, bool hasClip)
3004 : fImage(std::move(image))
3005 , fSrcRect(srcRect)
3006 , fDstRect(dstRect)
3007 , fMatrixIndex(matrixIndex)
3008 , fAlpha(alpha)
3009 , fAAFlags(aaFlags)
3010 , fHasClip(hasClip) {}
3011
3012SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3013 const SkRect& dstRect, float alpha, unsigned aaFlags)
3014 : fImage(std::move(image))
3015 , fSrcRect(srcRect)
3016 , fDstRect(dstRect)
3017 , fAlpha(alpha)
3018 , fAAFlags(aaFlags) {}
3019
3020///////////////////////////////////////////////////////////////////////////////
3021
Mike Reed5df49342016-11-12 08:06:55 -06003022std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003023 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003024 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003025 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003026 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003027
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003028 SkBitmap bitmap;
3029 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003030 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003031 }
Mike Reed12f77342017-11-08 11:19:52 -05003032
3033 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003034 std::make_unique<SkCanvas>(bitmap, *props) :
3035 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003036}
reedd5fa1a42014-08-09 11:08:05 -07003037
3038///////////////////////////////////////////////////////////////////////////////
3039
Florin Malitaee424ac2016-12-01 12:47:59 -05003040SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003041 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003042
Florin Malita439ace92016-12-02 12:05:41 -05003043SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003044 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003045
Herb Derbyefe39bc2018-05-01 17:06:20 -04003046SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003047 : INHERITED(device) {}
3048
Florin Malitaee424ac2016-12-01 12:47:59 -05003049SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3050 (void)this->INHERITED::getSaveLayerStrategy(rec);
3051 return kNoLayer_SaveLayerStrategy;
3052}
3053
Mike Reed148b7fd2018-12-18 17:38:18 -05003054bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3055 return false;
3056}
3057
Florin Malitaee424ac2016-12-01 12:47:59 -05003058///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003059
reed73603f32016-09-20 08:42:38 -07003060static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3061static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3062static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3063static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3064static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3065static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003066
3067///////////////////////////////////////////////////////////////////////////////////////////////////
3068
3069SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3070 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003071 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003072 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003073 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003074 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003075 clip.setEmpty();
3076 }
3077
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003078 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003079 return handle;
3080 }
3081 return nullptr;
3082}
3083
3084static bool install(SkBitmap* bm, const SkImageInfo& info,
3085 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003086 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003087}
3088
3089SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3090 SkBitmap* bm) {
3091 SkRasterHandleAllocator::Rec rec;
3092 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3093 return nullptr;
3094 }
3095 return rec.fHandle;
3096}
3097
3098std::unique_ptr<SkCanvas>
3099SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3100 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003101 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003102 return nullptr;
3103 }
3104
3105 SkBitmap bm;
3106 Handle hndl;
3107
3108 if (rec) {
3109 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3110 } else {
3111 hndl = alloc->allocBitmap(info, &bm);
3112 }
3113 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3114}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003115
3116///////////////////////////////////////////////////////////////////////////////////////////////////