blob: af6b269e71d912625d1a7955298baa40d21b139d [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Brian Osmanf11e3312020-03-24 14:57:38 -040020#include "include/effects/SkRuntimeEffect.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/private/SkNx.h"
22#include "include/private/SkTo.h"
23#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040024#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/core/SkBitmapDevice.h"
26#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Reedeb1d5a22020-04-14 09:16:40 -040035#include "src/core/SkMarkerStack.h"
Mike Reed07d32b42020-01-23 11:06:20 -050036#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "src/core/SkMatrixUtils.h"
38#include "src/core/SkPaintPriv.h"
39#include "src/core/SkRasterClip.h"
40#include "src/core/SkSpecialImage.h"
41#include "src/core/SkStrikeCache.h"
42#include "src/core/SkTLazy.h"
43#include "src/core/SkTextFormatParams.h"
44#include "src/core/SkTraceEvent.h"
Mike Reedba962562020-03-12 20:33:21 -040045#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050046#include "src/image/SkImage_Base.h"
47#include "src/image/SkSurface_Base.h"
48#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040049
bungemand3ebb482015-08-05 13:57:49 -070050#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050053#include "include/gpu/GrContext.h"
54#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000055#endif
56
reede3b38ce2016-01-08 09:18:44 -080057#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050058#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080059
Mike Klein1bb7e232019-12-10 08:58:52 -060060// This is a test: static_assert with no message is a c++17 feature,
61// and std::max() is constexpr only since the c++14 stdlib.
62static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000063
Mike Reed139e5e02017-03-08 11:29:33 -050064///////////////////////////////////////////////////////////////////////////////////////////////////
65
reedc83a2972015-07-16 07:40:45 -070066/*
67 * Return true if the drawing this rect would hit every pixels in the canvas.
68 *
69 * Returns false if
70 * - rect does not contain the canvas' bounds
71 * - paint is not fill
72 * - paint would blur or otherwise change the coverage of the rect
73 */
74bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
75 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070076 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
77 (int)kNone_ShaderOverrideOpacity,
78 "need_matching_enums0");
79 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
80 (int)kOpaque_ShaderOverrideOpacity,
81 "need_matching_enums1");
82 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
83 (int)kNotOpaque_ShaderOverrideOpacity,
84 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070085
86 const SkISize size = this->getBaseLayerSize();
87 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050088
89 // if we're clipped at all, we can't overwrite the entire surface
90 {
91 SkBaseDevice* base = this->getDevice();
92 SkBaseDevice* top = this->getTopDevice();
93 if (base != top) {
94 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
95 }
96 if (!base->clipIsWideOpen()) {
97 return false;
98 }
reedc83a2972015-07-16 07:40:45 -070099 }
100
101 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700102 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700103 return false; // conservative
104 }
halcanaryc5769b22016-08-10 07:13:21 -0700105
106 SkRect devRect;
107 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
108 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700109 return false;
110 }
111 }
112
113 if (paint) {
114 SkPaint::Style paintStyle = paint->getStyle();
115 if (!(paintStyle == SkPaint::kFill_Style ||
116 paintStyle == SkPaint::kStrokeAndFill_Style)) {
117 return false;
118 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400119 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700120 return false; // conservative
121 }
122 }
123 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
124}
125
126///////////////////////////////////////////////////////////////////////////////////////////////////
127
reed@google.comda17f752012-08-16 18:27:05 +0000128// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129//#define SK_TRACE_SAVERESTORE
130
131#ifdef SK_TRACE_SAVERESTORE
132 static int gLayerCounter;
133 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
134 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
135
136 static int gRecCounter;
137 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
138 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
139
140 static int gCanvasCounter;
141 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
142 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
143#else
144 #define inc_layer()
145 #define dec_layer()
146 #define inc_rec()
147 #define dec_rec()
148 #define inc_canvas()
149 #define dec_canvas()
150#endif
151
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000152typedef SkTLazy<SkPaint> SkLazyPaint;
153
reedc83a2972015-07-16 07:40:45 -0700154void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000155 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700156 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
157 ? SkSurface::kDiscard_ContentChangeMode
158 : SkSurface::kRetain_ContentChangeMode);
159 }
160}
161
162void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
163 ShaderOverrideOpacity overrideOpacity) {
164 if (fSurfaceBase) {
165 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
166 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
167 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
168 // and therefore we don't care which mode we're in.
169 //
170 if (fSurfaceBase->outstandingImageSnapshot()) {
171 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
172 mode = SkSurface::kDiscard_ContentChangeMode;
173 }
174 }
175 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000176 }
177}
178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000181/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 The clip/matrix/proc are fields that reflect the top of the save/restore
183 stack. Whenever the canvas changes, it marks a dirty flag, and then before
184 these are used (assuming we're not on a layer) we rebuild these cache
185 values: they reflect the top of the save stack, but translated and clipped
186 by the device's XY offset and bitmap-bounds.
187*/
188struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400189 DeviceCM* fNext;
190 sk_sp<SkBaseDevice> fDevice;
191 SkRasterClip fClip;
192 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
193 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400194 sk_sp<SkImage> fClipImage;
195 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196
Florin Malita53f77bd2017-04-28 13:48:37 -0400197 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000198 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700199 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400200 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500201 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700202 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000203 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400204 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400205 {}
reed@google.com4b226022011-01-11 18:32:13 +0000206
mtkleinfeaadee2015-04-08 11:25:48 -0700207 void reset(const SkIRect& bounds) {
208 SkASSERT(!fPaint);
209 SkASSERT(!fNext);
210 SkASSERT(fDevice);
211 fClip.setRect(bounds);
212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213};
214
Mike Reed148b7fd2018-12-18 17:38:18 -0500215namespace {
216// Encapsulate state needed to restore from saveBehind()
217struct BackImage {
218 sk_sp<SkSpecialImage> fImage;
219 SkIPoint fLoc;
220};
221}
222
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223/* This is the record we keep for each save/restore level in the stack.
224 Since a level optionally copies the matrix and/or stack, we have pointers
225 for these fields. If the value is copied for this level, the copy is
226 stored in the ...Storage field, and the pointer points to that. If the
227 value is not copied for this level, we ignore ...Storage, and just point
228 at the corresponding value in the previous level in the stack.
229*/
230class SkCanvas::MCRec {
231public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500232 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 /* If there are any layers in the stack, this points to the top-most
234 one that is at or below this level in the stack (so we know what
235 bitmap/device to draw into from this level. This value is NOT
236 reference counted, since the real owner is either our fLayer field,
237 or a previous one in a lower level.)
238 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500239 DeviceCM* fTopLayer;
240 std::unique_ptr<BackImage> fBackImage;
241 SkConservativeClip fRasterClip;
Brian Osmand8f611d2020-04-21 15:43:47 -0400242 SkM44 fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500243 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
Mike Reeda1361362017-03-07 09:37:29 -0500245 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700246 fLayer = nullptr;
247 fTopLayer = nullptr;
Brian Osmand8f611d2020-04-21 15:43:47 -0400248 fMatrix.setIdentity();
reed2ff1fce2014-12-11 07:07:37 -0800249 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700250
reedd9544982014-09-09 18:46:22 -0700251 // don't bother initializing fNext
252 inc_rec();
253 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400254 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700255 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700256 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800257 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 // don't bother initializing fNext
260 inc_rec();
261 }
262 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700263 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 dec_rec();
265 }
mtkleinfeaadee2015-04-08 11:25:48 -0700266
267 void reset(const SkIRect& bounds) {
268 SkASSERT(fLayer);
269 SkASSERT(fDeferredSaveCount == 0);
270
Brian Osmand8f611d2020-04-21 15:43:47 -0400271 fMatrix.setIdentity();
mtkleinfeaadee2015-04-08 11:25:48 -0700272 fRasterClip.setRect(bounds);
273 fLayer->reset(bounds);
274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275};
276
Mike Reeda1361362017-03-07 09:37:29 -0500277class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278public:
Mike Reeda1361362017-03-07 09:37:29 -0500279 SkDrawIter(SkCanvas* canvas)
280 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
281 {}
reed@google.com4b226022011-01-11 18:32:13 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000284 const DeviceCM* rec = fCurrLayer;
285 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400286 fDevice = rec->fDevice.get();
287 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700289 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 return true;
291 }
292 return false;
293 }
reed@google.com4b226022011-01-11 18:32:13 +0000294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000296
Mike Reed99330ba2017-02-22 11:01:08 -0500297 SkBaseDevice* fDevice;
298
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 const DeviceCM* fCurrLayer;
301 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302};
303
Florin Malita713b8ef2017-04-28 10:57:24 -0400304#define FOR_EACH_TOP_DEVICE( code ) \
305 do { \
306 DeviceCM* layer = fMCRec->fTopLayer; \
307 while (layer) { \
308 SkBaseDevice* device = layer->fDevice.get(); \
309 if (device) { \
310 code; \
311 } \
312 layer = layer->fNext; \
313 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500314 } while (0)
315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316/////////////////////////////////////////////////////////////////////////////
317
reeddbc3cef2015-04-29 12:18:57 -0700318/**
319 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700320 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700321 */
reedd053ce92016-03-22 10:17:23 -0700322static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700323 SkImageFilter* imgf = paint.getImageFilter();
324 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700325 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700326 }
327
reedd053ce92016-03-22 10:17:23 -0700328 SkColorFilter* imgCFPtr;
329 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700330 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700331 }
reedd053ce92016-03-22 10:17:23 -0700332 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700333
334 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700335 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700336 // there is no existing paint colorfilter, so we can just return the imagefilter's
337 return imgCF;
338 }
339
340 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
341 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500342 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700343}
344
senorblanco87e066e2015-10-28 11:23:36 -0700345/**
346 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
347 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
348 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
349 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
350 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
351 * conservative "effective" bounds based on the settings in the paint... with one exception. This
352 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
353 * deliberately ignored.
354 */
355static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
356 const SkRect& rawBounds,
357 SkRect* storage) {
358 SkPaint tmpUnfiltered(paint);
359 tmpUnfiltered.setImageFilter(nullptr);
360 if (tmpUnfiltered.canComputeFastBounds()) {
361 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
362 } else {
363 return rawBounds;
364 }
365}
366
Mike Reed38992392019-07-30 10:48:15 -0400367class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368public:
senorblanco87e066e2015-10-28 11:23:36 -0700369 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
370 // paint. It's used to determine the size of the offscreen layer for filters.
371 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400372 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
373 bool skipLayerForImageFilter = false,
374 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400376 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000377 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700378 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
Mike Reed38992392019-07-30 10:48:15 -0400380 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
381 SkASSERT(!fLazyPaint.isValid());
382 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700383 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700384 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700385 fPaint = paint;
386 }
387
388 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700389 /**
390 * We implement ImageFilters for a given draw by creating a layer, then applying the
391 * imagefilter to the pixels of that layer (its backing surface/image), and then
392 * we call restore() to xfer that layer to the main canvas.
393 *
394 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
395 * 2. Generate the src pixels:
396 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
397 * return (fPaint). We then draw the primitive (using srcover) into a cleared
398 * buffer/surface.
399 * 3. Restore the layer created in #1
400 * The imagefilter is passed the buffer/surface from the layer (now filled with the
401 * src pixels of the primitive). It returns a new "filtered" buffer, which we
402 * draw onto the previous layer using the xfermode from the original paint.
403 */
Mike Reed38992392019-07-30 10:48:15 -0400404
405 SkPaint restorePaint;
406 restorePaint.setImageFilter(fPaint->refImageFilter());
407 restorePaint.setBlendMode(fPaint->getBlendMode());
408
senorblanco87e066e2015-10-28 11:23:36 -0700409 SkRect storage;
410 if (rawBounds) {
411 // Make rawBounds include all paint outsets except for those due to image filters.
412 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
413 }
Mike Reed38992392019-07-30 10:48:15 -0400414 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700415 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700416 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000417
Mike Reed38992392019-07-30 10:48:15 -0400418 // Remove the restorePaint fields from our "working" paint
419 SkASSERT(!fLazyPaint.isValid());
420 SkPaint* paint = fLazyPaint.set(origPaint);
421 paint->setImageFilter(nullptr);
422 paint->setBlendMode(SkBlendMode::kSrcOver);
423 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000424 }
425 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000426
Mike Reed38992392019-07-30 10:48:15 -0400427 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700428 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000429 fCanvas->internalRestore();
430 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000431 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000433
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434 const SkPaint& paint() const {
435 SkASSERT(fPaint);
436 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439private:
Mike Reed38992392019-07-30 10:48:15 -0400440 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400442 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000443 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700444 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445};
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447////////// macros to place around the internal draw calls //////////////////
448
Mike Reed38992392019-07-30 10:48:15 -0400449#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700450 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400451 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
452 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800453
454
Mike Reed38992392019-07-30 10:48:15 -0400455#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000456 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400457 AutoLayerForImageFilter draw(this, paint, true); \
458 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000459
Mike Reed38992392019-07-30 10:48:15 -0400460#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000461 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400462 AutoLayerForImageFilter draw(this, paint, false, bounds); \
463 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000464
Mike Reed38992392019-07-30 10:48:15 -0400465#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700466 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400467 AutoLayerForImageFilter draw(this, paint, false, bounds); \
468 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700469
Mike Reed38992392019-07-30 10:48:15 -0400470#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471
472////////////////////////////////////////////////////////////////////////////
473
msarettfbfa2582016-08-12 08:29:08 -0700474static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
475 if (bounds.isEmpty()) {
476 return SkRect::MakeEmpty();
477 }
478
479 // Expand bounds out by 1 in case we are anti-aliasing. We store the
480 // bounds as floats to enable a faster quick reject implementation.
481 SkRect dst;
482 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
483 return dst;
484}
485
mtkleinfeaadee2015-04-08 11:25:48 -0700486void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
487 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700488 fMCRec->reset(bounds);
489
490 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500491 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400492 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700493 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700494 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700495}
496
Hal Canary363a3f82018-10-04 11:04:48 -0400497void SkCanvas::init(sk_sp<SkBaseDevice> device) {
Mike Reedeb1d5a22020-04-14 09:16:40 -0400498 fMarkerStack = sk_make_sp<SkMarkerStack>();
499
reed2ff1fce2014-12-11 07:07:37 -0800500 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501
502 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500503 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500504 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700505 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506
reeda499f902015-05-01 09:34:31 -0700507 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
508 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Brian Osmand8f611d2020-04-21 15:43:47 -0400509 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix.asM33(), nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512
halcanary96fcdcc2015-08-27 07:41:13 -0700513 fSurfaceBase = nullptr;
Mike Reed4c057862020-05-20 20:57:55 -0400514 fDeviceClipBounds = {0, 0, 0, 0};
reed@android.comf2b98d62010-12-20 18:26:13 +0000515
reedf92c8662014-08-18 08:02:43 -0700516 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700517 // The root device and the canvas should always have the same pixel geometry
518 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800519 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700520 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500521
Mike Reedc42a1cd2017-02-14 14:25:14 -0500522 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
Mike Reedeb1d5a22020-04-14 09:16:40 -0400523
524 device->setMarkerStack(fMarkerStack.get());
reedf92c8662014-08-18 08:02:43 -0700525 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400526
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500527 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528}
529
reed@google.comcde92112011-07-06 20:00:52 +0000530SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000531 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700532 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000534 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000535
Hal Canary363a3f82018-10-04 11:04:48 -0400536 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000537}
538
reed96a857e2015-01-25 10:33:58 -0800539SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000540 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800541 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000542{
543 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400544 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500545 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700546}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000547
Hal Canary363a3f82018-10-04 11:04:48 -0400548SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700549 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700550 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700551{
552 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700553
Mike Reed566e53c2017-03-10 10:49:45 -0500554 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400555 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700556}
557
Herb Derbyefe39bc2018-05-01 17:06:20 -0400558SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000559 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700560 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000561{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700563
Hal Canary363a3f82018-10-04 11:04:48 -0400564 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700565}
566
reed4a8126e2014-09-22 07:29:03 -0700567SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700568 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700569 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700570{
571 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700572
Mike Reed910ca0f2018-04-25 13:04:05 -0400573 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400574 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700575}
reed29c857d2014-09-21 10:25:07 -0700576
Mike Reed356f7c22017-01-10 11:58:39 -0500577SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
578 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700579 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
580 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500581 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700582{
583 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700584
Mike Reed910ca0f2018-04-25 13:04:05 -0400585 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400586 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587}
588
Mike Reed356f7c22017-01-10 11:58:39 -0500589SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
590
Matt Sarett31f99ce2017-04-11 08:46:01 -0400591#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
592SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
593 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
594 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
595 , fAllocator(nullptr)
596{
597 inc_canvas();
598
599 SkBitmap tmp(bitmap);
600 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400601 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400602 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400603}
604#endif
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606SkCanvas::~SkCanvas() {
607 // free up the contents of our deque
608 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 this->internalRestore(); // restore the last, since we're going away
611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 dec_canvas();
613}
614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615///////////////////////////////////////////////////////////////////////////////
616
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000617void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700618 this->onFlush();
619}
620
621void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000622 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000623 if (device) {
624 device->flush();
625 }
626}
627
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500628SkSurface* SkCanvas::getSurface() const {
629 return fSurfaceBase;
630}
631
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000632SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000633 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000634 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
635}
636
senorblancoafc7cce2016-02-02 18:44:15 -0800637SkIRect SkCanvas::getTopLayerBounds() const {
638 SkBaseDevice* d = this->getTopDevice();
639 if (!d) {
640 return SkIRect::MakeEmpty();
641 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500642 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800643}
644
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000645SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000647 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400649 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650}
651
Florin Malita0ed3b642017-01-13 16:56:38 +0000652SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400653 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000654}
655
Mike Reed353196f2017-07-21 11:01:18 -0400656bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000657 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400658 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000659}
660
Mike Reed353196f2017-07-21 11:01:18 -0400661bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
662 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400663}
664
665bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
666 SkPixmap pm;
667 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
668}
669
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000670bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400671 SkPixmap pm;
672 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700673 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 }
675 return false;
676}
677
Matt Sarett03dd6d52017-01-23 12:15:09 -0500678bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000679 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000680 SkBaseDevice* device = this->getDevice();
681 if (!device) {
682 return false;
683 }
684
Matt Sarett03dd6d52017-01-23 12:15:09 -0500685 // This check gives us an early out and prevents generation ID churn on the surface.
686 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
687 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400688 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500689 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000690 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000691
Matt Sarett03dd6d52017-01-23 12:15:09 -0500692 // Tell our owning surface to bump its generation ID.
693 const bool completeOverwrite =
694 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700695 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700696
Matt Sarett03dd6d52017-01-23 12:15:09 -0500697 // This can still fail, most notably in the case of a invalid color type or alpha type
698 // conversion. We could pull those checks into this function and avoid the unnecessary
699 // generation ID bump. But then we would be performing those checks twice, since they
700 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400701 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000702}
reed@google.com51df9e32010-12-23 19:29:18 +0000703
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704//////////////////////////////////////////////////////////////////////////////
705
reed2ff1fce2014-12-11 07:07:37 -0800706void SkCanvas::checkForDeferredSave() {
707 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800708 this->doSave();
709 }
710}
711
reedf0090cb2014-11-26 08:55:51 -0800712int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800713#ifdef SK_DEBUG
714 int count = 0;
715 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
716 for (;;) {
717 const MCRec* rec = (const MCRec*)iter.next();
718 if (!rec) {
719 break;
720 }
721 count += 1 + rec->fDeferredSaveCount;
722 }
723 SkASSERT(count == fSaveCount);
724#endif
725 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800726}
727
728int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800729 fSaveCount += 1;
730 fMCRec->fDeferredSaveCount += 1;
731 return this->getSaveCount() - 1; // return our prev value
732}
733
734void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800735 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700736
737 SkASSERT(fMCRec->fDeferredSaveCount > 0);
738 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800739 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800740}
741
742void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800743 if (fMCRec->fDeferredSaveCount > 0) {
744 SkASSERT(fSaveCount > 1);
745 fSaveCount -= 1;
746 fMCRec->fDeferredSaveCount -= 1;
747 } else {
748 // check for underflow
749 if (fMCStack.count() > 1) {
750 this->willRestore();
751 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700752 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800753 this->internalRestore();
754 this->didRestore();
755 }
reedf0090cb2014-11-26 08:55:51 -0800756 }
757}
758
759void SkCanvas::restoreToCount(int count) {
760 // sanity check
761 if (count < 1) {
762 count = 1;
763 }
mtkleinf0f14112014-12-12 08:46:25 -0800764
reedf0090cb2014-11-26 08:55:51 -0800765 int n = this->getSaveCount() - count;
766 for (int i = 0; i < n; ++i) {
767 this->restore();
768 }
769}
770
reed2ff1fce2014-12-11 07:07:37 -0800771void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700773 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000775
Mike Reedc42a1cd2017-02-14 14:25:14 -0500776 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777}
778
reed4960eee2015-12-18 07:09:18 -0800779bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400780 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781}
782
reed4960eee2015-12-18 07:09:18 -0800783bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700784 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400785 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
786 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
787 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
788 // filter does not require a decomposed CTM matrix, the filter space and device space are the
789 // same. When it has been decomposed, we want the original image filter node to process the
790 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
791 // longer the original filter, but has the remainder matrix baked into it, and passing in the
792 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
793 // to the original filter node (barring inflation from consecutive calls to mapRect). While
794 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
795 // passing getDeviceClipBounds() to 'imageFilter' is correct.
796 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
797 // be important to more accurately calculate the clip bounds in the layer space for the original
798 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500799 SkIRect clipBounds = this->getDeviceClipBounds();
800 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000801 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000802 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000803
Brian Osmand8f611d2020-04-21 15:43:47 -0400804 const SkMatrix& ctm = fMCRec->fMatrix.asM33(); // this->getTotalMatrix()
reed96e657d2015-03-10 17:30:07 -0700805
Robert Phillips12078432018-05-17 11:17:39 -0400806 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
807 // If the image filter DAG affects transparent black then we will need to render
808 // out to the clip bounds
809 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000810 }
Robert Phillips12078432018-05-17 11:17:39 -0400811
812 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700813 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700815 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400816 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400818 inputSaveLayerBounds = clipBounds;
819 }
820
821 if (imageFilter) {
822 // expand the clip bounds by the image filter DAG to include extra content that might
823 // be required by the image filters.
824 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
825 SkImageFilter::kReverse_MapDirection,
826 &inputSaveLayerBounds);
827 }
828
829 SkIRect clippedSaveLayerBounds;
830 if (bounds) {
831 // For better or for worse, user bounds currently act as a hard clip on the layer's
832 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
833 clippedSaveLayerBounds = inputSaveLayerBounds;
834 } else {
835 // If there are no user bounds, we don't want to artificially restrict the resulting
836 // layer bounds, so allow the expanded clip bounds free reign.
837 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800839
840 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400841 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800842 if (BoundsAffectsClip(saveLayerFlags)) {
843 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
844 fMCRec->fRasterClip.setEmpty();
845 fDeviceClipBounds.setEmpty();
846 }
847 return false;
848 }
Robert Phillips12078432018-05-17 11:17:39 -0400849 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850
reed4960eee2015-12-18 07:09:18 -0800851 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700852 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400853 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
854 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 }
856
857 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400858 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000859 }
Robert Phillips12078432018-05-17 11:17:39 -0400860
junov@chromium.orga907ac32012-02-24 21:54:07 +0000861 return true;
862}
863
reed4960eee2015-12-18 07:09:18 -0800864int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
865 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000866}
867
Cary Clarke041e312018-03-06 13:00:52 -0500868int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700869 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400870 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
871 // no need for the layer (or any of the draws until the matching restore()
872 this->save();
873 this->clipRect({0,0,0,0});
874 } else {
875 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
876 fSaveCount += 1;
877 this->internalSaveLayer(rec, strategy);
878 }
reed4960eee2015-12-18 07:09:18 -0800879 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800880}
881
Mike Reed148b7fd2018-12-18 17:38:18 -0500882int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
883 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
884 // Assuming clips never expand, if the request bounds is outside of the current clip
885 // there is no need to copy/restore the area, so just devolve back to a regular save.
886 this->save();
887 } else {
888 bool doTheWork = this->onDoSaveBehind(bounds);
889 fSaveCount += 1;
890 this->internalSave();
891 if (doTheWork) {
892 this->internalSaveBehind(bounds);
893 }
894 }
895 return this->getSaveCount() - 1;
896}
897
reeda2217ef2016-07-20 06:04:34 -0700898void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500899 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500900 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400901 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
902 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400903 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500904 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
905 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400906 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400907 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
908 // This means that we only have to copy a dst-sized block of pixels out of src and translate
909 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400910 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
911 dstOrigin.y() - src->getOrigin().y(),
912 dst->width(), dst->height());
913 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400914 return;
915 }
916
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400917 auto special = src->snapSpecial(snapBounds);
918 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400919 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
920 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400921 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
922 }
923 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400924 }
925
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400926 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
927 // by the backdrop filter.
928 SkMatrix toRoot, layerMatrix;
929 SkSize scale;
930 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
931 toRoot = SkMatrix::I();
932 layerMatrix = ctm;
933 } else if (ctm.decomposeScale(&scale, &toRoot)) {
Mike Reed1f607332020-05-21 12:11:27 -0400934 layerMatrix = SkMatrix::Scale(scale.fWidth, scale.fHeight);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400935 } else {
936 // Perspective, for now, do no scaling of the layer itself.
937 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
938 // the matrix, e.g. based on the midpoint of the near/far planes?
939 toRoot = ctm;
940 layerMatrix = SkMatrix::I();
941 }
942
943 // We have to map the dst bounds from the root space into the layer space where filtering will
944 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
945 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
946 // is a safe, conservative estimate.
947 SkMatrix fromRoot;
948 if (!toRoot.invert(&fromRoot)) {
949 return;
950 }
951
952 // This represents what the backdrop filter needs to produce in the layer space, and is sized
953 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
954 SkIRect layerTargetBounds = fromRoot.mapRect(
955 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
956 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
957 // require some extra input pixels.
958 SkIRect layerInputBounds = filter->filterBounds(
959 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
960 &layerTargetBounds);
961
962 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400963 // be the conservative contents required to fill a layerInputBounds-sized surface with the
964 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400965 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
966 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
967 if (!backdropBounds.intersect(srcDevRect)) {
968 return;
969 }
970
971 auto special = src->snapSpecial(backdropBounds);
972 if (!special) {
973 return;
974 }
975
976 SkColorType colorType = src->imageInfo().colorType();
977 if (colorType == kUnknown_SkColorType) {
978 colorType = kRGBA_8888_SkColorType;
979 }
980 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400981
982 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400983 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400984 // Drawing the temporary and final filtered image requires a higher filter quality if the
985 // 'toRoot' transformation is not identity, in order to minimize the impact on already
986 // rendered edges/content.
987 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
988 p.setFilterQuality(kHigh_SkFilterQuality);
989
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400990 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
991 // and stored in a temporary surface, which is then used as the input to the actual filter.
992 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
993 if (!tmpSurface) {
994 return;
995 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400996
997 auto tmpCanvas = tmpSurface->getCanvas();
998 tmpCanvas->clear(SK_ColorTRANSPARENT);
999 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1000 // space, then maps from root space into the layer space, then maps it so the input layer's
1001 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1002 // performed on backdropBounds.
1003 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1004 tmpCanvas->concat(fromRoot);
1005 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001006
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001007 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1008 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1009 special = tmpSurface->makeImageSnapshot();
1010 } else {
1011 // Since there is no extra transform that was done, update the input bounds to reflect
1012 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1013 // was equal to backdropBounds before it was made relative to the src device and cropped.
1014 // When we use the original snapped image directly, just map the update backdrop bounds
1015 // back into the shared layer space
1016 layerInputBounds = backdropBounds;
1017 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001018
1019 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1020 // draw will be 1-1 so there is no need to increase filter quality.
1021 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001022 }
1023
1024 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1025 // layer space. This has to further offset everything so that filter evaluation thinks the
1026 // source image's top left corner is (0, 0).
1027 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1028 // this can be simplified.
1029 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1030 SkMatrix filterCTM = layerMatrix;
1031 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1032 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1033
1034 SkIPoint offset;
1035 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001036 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001037 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1038 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1039 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1040 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001041 offset += layerInputBounds.topLeft();
1042
1043 // Manually setting the device's CTM requires accounting for the device's origin.
1044 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001045 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001046 // a global CTM instead of a device CTM.
1047 SkMatrix dstCTM = toRoot;
1048 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001049 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050
1051 // And because devices don't have a special-image draw function that supports arbitrary
1052 // matrices, we are abusing the asImage() functionality here...
1053 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001054 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001055 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001056 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001057 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1058 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001059 }
robertphillips7354a4b2015-12-16 05:08:27 -08001060}
reed70ee31b2015-12-10 13:44:45 -08001061
Mike Kleine083f7c2018-02-07 12:54:27 -05001062static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001063 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001064 if (prev.bytesPerPixel() <= 4 &&
1065 prev.colorType() != kRGBA_8888_SkColorType &&
1066 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001067 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1068 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1069 ct = kN32_SkColorType;
1070 }
1071 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001072}
1073
reed4960eee2015-12-18 07:09:18 -08001074void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001075 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001076 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001077 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1078
Michael Ludwigeced98b2020-03-03 10:39:41 -05001079 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1080 // saveLayer ignores mask filters, so force it to null
1081 if (paint.get() && paint->getMaskFilter()) {
1082 paint.writable()->setMaskFilter(nullptr);
1083 }
1084
Mike Reed5532c2a2019-02-23 12:00:32 -05001085 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1086 // regardless of any hint-rect from the caller. skbug.com/8783
1087 if (rec.fBackdrop) {
1088 bounds = nullptr;
1089 }
1090
Michael Ludwigeced98b2020-03-03 10:39:41 -05001091 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
Brian Osmand8f611d2020-04-21 15:43:47 -04001092 SkMatrix stashedMatrix = fMCRec->fMatrix.asM33();
Robert Phillips3d0e8502018-04-20 10:27:27 -04001093 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001094
reed8c30a812016-04-20 16:36:51 -07001095 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001096 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1097 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1098 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001099 *
1100 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001101 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1102 * if necessary.
1103 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1104 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001105 * 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 -04001106 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001107 * of the original imagefilter, and draw that (via drawSprite)
1108 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1109 *
1110 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1111 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1112 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001113 if (imageFilter) {
1114 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001115 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1116 &modifiedCTM);
1117 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001118 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001119 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001120 modifiedRec = fMCRec;
1121 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001122 imageFilter = modifiedFilter.get();
1123 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001124 }
1125 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1126 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001127 }
reed8c30a812016-04-20 16:36:51 -07001128
junov@chromium.orga907ac32012-02-24 21:54:07 +00001129 // do this before we create the layer. We don't call the public save() since
1130 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001131 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001132
junov@chromium.orga907ac32012-02-24 21:54:07 +00001133 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001134 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001135 if (modifiedRec) {
1136 // In this case there will be no layer in which to stash the matrix so we need to
1137 // revert the prior MCRec to its earlier state.
Brian Osmand8f611d2020-04-21 15:43:47 -04001138 modifiedRec->fMatrix = SkM44(stashedMatrix);
Robert Phillips3d0e8502018-04-20 10:27:27 -04001139 }
reed2ff1fce2014-12-11 07:07:37 -08001140 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 }
1142
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001143 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1144 // the clipRectBounds() call above?
1145 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001146 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001147 }
1148
robertphillips5139e502016-07-19 05:10:40 -07001149 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001150 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001151 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001152 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001153 }
reedb2db8982014-11-13 12:41:02 -08001154
Mike Kleine083f7c2018-02-07 12:54:27 -05001155 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001156 if (rec.fSaveLayerFlags & kF16ColorType) {
1157 info = info.makeColorType(kRGBA_F16_SkColorType);
1158 }
reed129ed1c2016-02-22 06:42:31 -08001159
Hal Canary704cd322016-11-07 14:13:52 -05001160 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001161 {
Florin Malita4571e492019-07-16 10:25:58 -04001162 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
Florin Malitae55e5602020-05-01 09:33:49 -04001163
Xianzhu Wanga1ed96b2020-05-11 22:11:03 -07001164 SkPixelGeometry geo = saveLayerFlags & kPreserveLCDText_SaveLayerFlag
1165 ? fProps.pixelGeometry()
1166 : kUnknown_SkPixelGeometry;
Herb Derby41f4f312018-06-06 17:45:53 +00001167 const bool trackCoverage =
1168 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
Florin Malitae55e5602020-05-01 09:33:49 -04001169 const auto createInfo = SkBaseDevice::CreateInfo(info,
1170 geo,
1171 SkBaseDevice::kNever_TileUsage,
1172 trackCoverage,
1173 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001174 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1175 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001176 return;
reed61f501f2015-04-29 08:34:00 -07001177 }
Mike Reedeb1d5a22020-04-14 09:16:40 -04001178 newDevice->setMarkerStack(fMarkerStack.get());
bungeman@google.come25c6842011-08-17 14:53:54 +00001179 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001180 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181
Mike Reedb43a3e02017-02-11 10:18:58 -05001182 // only have a "next" if this new layer doesn't affect the clip (rare)
1183 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 fMCRec->fLayer = layer;
1185 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001186
Mike Reedc61abee2017-02-28 17:45:27 -05001187 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001188 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Brian Osmand8f611d2020-04-21 15:43:47 -04001189 fMCRec->fMatrix.asM33());
reeda2217ef2016-07-20 06:04:34 -07001190 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001191
Brian Osmanf8865052020-04-22 15:44:55 -04001192 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
Mike Reedc42a1cd2017-02-14 14:25:14 -05001193
1194 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1195 if (layer->fNext) {
1196 // need to punch a hole in the previous device, so we don't draw there, given that
1197 // the new top-layer will allow drawing to happen "below" it.
1198 SkRegion hole(ir);
1199 do {
1200 layer = layer->fNext;
1201 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1202 } while (layer->fNext);
1203 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204}
1205
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001206int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001207 if (0xFF == alpha) {
1208 return this->saveLayer(bounds, nullptr);
1209 } else {
1210 SkPaint tmpPaint;
1211 tmpPaint.setAlpha(alpha);
1212 return this->saveLayer(bounds, &tmpPaint);
1213 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001214}
1215
Mike Reed148b7fd2018-12-18 17:38:18 -05001216void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001217 SkBaseDevice* device = this->getTopDevice();
1218 if (nullptr == device) { // Do we still need this check???
1219 return;
1220 }
1221
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001222 // Map the local bounds into the top device's coordinate space (this is not
1223 // necessarily the full global CTM transform).
1224 SkIRect devBounds;
1225 if (localBounds) {
1226 SkRect tmp;
1227 device->localToDevice().mapRect(&tmp, *localBounds);
1228 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1229 devBounds.setEmpty();
1230 }
1231 } else {
1232 devBounds = device->devClipBounds();
1233 }
1234 if (devBounds.isEmpty()) {
1235 return;
1236 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001237
Michael Ludwigac352122019-08-28 21:03:05 +00001238 // This is getting the special image from the current device, which is then drawn into (both by
1239 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1240 // own device, we need to explicitly copy the back image contents so that its original content
1241 // is available when we splat it back later during restore.
1242 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001243 if (!backImage) {
1244 return;
1245 }
1246
1247 // we really need the save, so we can wack the fMCRec
1248 this->checkForDeferredSave();
1249
1250 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1251
1252 SkPaint paint;
1253 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001254 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001255}
1256
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257void SkCanvas::internalRestore() {
1258 SkASSERT(fMCStack.count() != 0);
1259
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001260 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 DeviceCM* layer = fMCRec->fLayer; // may be null
1262 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001263 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264
Mike Reed148b7fd2018-12-18 17:38:18 -05001265 // move this out before we do the actual restore
1266 auto backImage = std::move(fMCRec->fBackImage);
1267
Mike Reedeb1d5a22020-04-14 09:16:40 -04001268 fMarkerStack->restore(fMCRec);
Mike Reedb18e74d2020-01-16 13:58:22 -05001269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 // now do the normal restore()
1271 fMCRec->~MCRec(); // balanced in save()
1272 fMCStack.pop_back();
1273 fMCRec = (MCRec*)fMCStack.back();
1274
Mike Reedc42a1cd2017-02-14 14:25:14 -05001275 if (fMCRec) {
1276 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1277 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001278
Mike Reed148b7fd2018-12-18 17:38:18 -05001279 if (backImage) {
1280 SkPaint paint;
1281 paint.setBlendMode(SkBlendMode::kDstOver);
1282 const int x = backImage->fLoc.x();
1283 const int y = backImage->fLoc.y();
1284 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1285 nullptr, SkMatrix::I());
1286 }
1287
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1289 since if we're being recorded, we don't want to record this (the
1290 recorder will have already recorded the restore).
1291 */
bsalomon49f085d2014-09-05 13:34:00 -07001292 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001293 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001294 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001295 // At this point, 'layer' has been removed from the device stack, so the devices that
1296 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1297 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001298 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001299 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001300 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001301 delete layer;
reedb679ca82015-04-07 04:40:48 -07001302 } else {
1303 // we're at the root
reeda499f902015-05-01 09:34:31 -07001304 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001305 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001306 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001308 }
msarettfbfa2582016-08-12 08:29:08 -07001309
1310 if (fMCRec) {
Brian Osmand8f611d2020-04-21 15:43:47 -04001311 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
msarettfbfa2582016-08-12 08:29:08 -07001312 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314}
1315
reede8f30622016-03-23 18:59:25 -07001316sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001317 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001318 props = &fProps;
1319 }
1320 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001321}
1322
reede8f30622016-03-23 18:59:25 -07001323sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001324 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001325 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001326}
1327
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001328SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001329 return this->onImageInfo();
1330}
1331
1332SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001333 SkBaseDevice* dev = this->getDevice();
1334 if (dev) {
1335 return dev->imageInfo();
1336 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001337 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001338 }
1339}
1340
brianosman898235c2016-04-06 07:38:23 -07001341bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001342 return this->onGetProps(props);
1343}
1344
1345bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001346 SkBaseDevice* dev = this->getDevice();
1347 if (dev) {
1348 if (props) {
1349 *props = fProps;
1350 }
1351 return true;
1352 } else {
1353 return false;
1354 }
1355}
1356
reed6ceeebd2016-03-09 14:26:26 -08001357bool SkCanvas::peekPixels(SkPixmap* pmap) {
1358 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001359}
1360
reed884e97c2015-05-26 11:31:54 -07001361bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001362 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001363 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001364}
1365
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001366void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001367 SkPixmap pmap;
1368 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001369 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001370 }
1371 if (info) {
1372 *info = pmap.info();
1373 }
1374 if (rowBytes) {
1375 *rowBytes = pmap.rowBytes();
1376 }
1377 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001378 // If the caller requested the origin, they presumably are expecting the returned pixels to
1379 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1380 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1381 // instead of an origin, just don't expose the pixels in that case. Note that this means
1382 // that layers with complex coordinate spaces can still report their pixels if the caller
1383 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1384 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1385 *origin = this->getTopDevice()->getOrigin();
1386 } else {
1387 return nullptr;
1388 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001389 }
reed884e97c2015-05-26 11:31:54 -07001390 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001391}
1392
reed884e97c2015-05-26 11:31:54 -07001393bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001394 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001395 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001396}
1397
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399
Mike Reed8bcd1282019-03-13 16:51:54 -04001400// In our current design/features, we should never have a layer (src) in a different colorspace
1401// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1402// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1403// colorspace.
1404static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1405 SkASSERT(src == dst);
1406}
1407
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001408void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001409 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001411 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 paint = &tmp;
1413 }
reed@google.com4b226022011-01-11 18:32:13 +00001414
Mike Reed38992392019-07-30 10:48:15 -04001415 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001416
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001418 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001419 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1420 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001421 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001422 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001423 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1424 // so it should always be possible to use the relative origin. Once drawDevice() and
1425 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1426 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001427 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001428 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1429 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001430 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1431 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001432 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1433 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001434 }
reed@google.com76dd2772012-01-05 21:15:07 +00001435 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001436 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001437 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438 }
reeda2217ef2016-07-20 06:04:34 -07001439
Mike Reed38992392019-07-30 10:48:15 -04001440 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441}
1442
reed32704672015-12-16 08:27:10 -08001443/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001444
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001445void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001446 if (dx || dy) {
1447 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001448 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001449
reedfe69b502016-09-12 06:31:48 -07001450 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001451 // However, if either is non-finite, we might still complicate the matrix type,
1452 // so we still have to compute this.
Brian Osmand8f611d2020-04-21 15:43:47 -04001453 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
mtkleincbdf0072016-08-19 09:05:27 -07001454
Mike Reedc42a1cd2017-02-14 14:25:14 -05001455 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001456
reedfe69b502016-09-12 06:31:48 -07001457 this->didTranslate(dx,dy);
1458 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459}
1460
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001461void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001462 if (sx != 1 || sy != 1) {
1463 this->checkForDeferredSave();
1464 fMCRec->fMatrix.preScale(sx, sy);
1465
1466 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1467 // but pre-scaling by a non-finite does change it, so we have to recompute.
Brian Osmand8f611d2020-04-21 15:43:47 -04001468 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
Mike Reed9403c382020-01-13 14:40:56 +00001469
1470 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1471
1472 this->didScale(sx, sy);
1473 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001476void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001477 SkMatrix m;
1478 m.setRotate(degrees);
1479 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480}
1481
bungeman7438bfc2016-07-12 15:01:19 -07001482void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1483 SkMatrix m;
1484 m.setRotate(degrees, px, py);
1485 this->concat(m);
1486}
1487
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001488void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001489 SkMatrix m;
1490 m.setSkew(sx, sy);
1491 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001492}
1493
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001494void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001495 if (matrix.isIdentity()) {
1496 return;
1497 }
1498
reed2ff1fce2014-12-11 07:07:37 -08001499 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001500 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001501
Brian Osmand8f611d2020-04-21 15:43:47 -04001502 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
Mike Reed7627fa52017-02-08 10:07:53 -05001503
Mike Reed7627fa52017-02-08 10:07:53 -05001504 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001505
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001506 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001507}
1508
Mike Reed2f92c932020-04-06 15:44:17 -04001509void SkCanvas::internalConcat44(const SkM44& m) {
Mike Reed403c8072020-01-08 10:40:39 -05001510 this->checkForDeferredSave();
1511
Mike Reed3ef77dd2020-04-06 10:41:09 -04001512 fMCRec->fMatrix.preConcat(m);
Mike Reed403c8072020-01-08 10:40:39 -05001513
Brian Osmand8f611d2020-04-21 15:43:47 -04001514 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix);
Mike Reed403c8072020-01-08 10:40:39 -05001515
1516 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed2f92c932020-04-06 15:44:17 -04001517}
Mike Reed403c8072020-01-08 10:40:39 -05001518
Mike Reed2f92c932020-04-06 15:44:17 -04001519void SkCanvas::concat(const SkM44& m) {
1520 this->internalConcat44(m);
Mike Reeda735ad92020-04-06 21:32:43 -04001521 // notify subclasses
Mike Reeda735ad92020-04-06 21:32:43 -04001522 this->didConcat44(m);
Mike Reedee3216d2020-01-17 17:35:04 -05001523}
1524
reed8c30a812016-04-20 16:36:51 -07001525void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
Brian Osmand8f611d2020-04-21 15:43:47 -04001526 fMCRec->fMatrix = SkM44(matrix);
msarett9da5a5a2016-08-19 08:38:36 -07001527 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001528
Mike Reedc42a1cd2017-02-14 14:25:14 -05001529 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001530}
1531
1532void SkCanvas::setMatrix(const SkMatrix& matrix) {
1533 this->checkForDeferredSave();
1534 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001535 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536}
1537
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001539 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540}
1541
Brian Osman548de742020-04-24 12:02:25 -04001542void SkCanvas::markCTM(const char* name) {
Brian Osman84aa92e2020-04-30 10:43:33 -04001543 if (SkCanvasPriv::ValidateMarker(name)) {
Brian Osman548de742020-04-24 12:02:25 -04001544 fMarkerStack->setMarker(SkOpts::hash_fn(name, strlen(name), 0),
1545 this->getLocalToDevice(), fMCRec);
1546 this->onMarkCTM(name);
1547 }
Mike Reed7fe6ee32020-04-09 12:35:09 -04001548}
1549
Brian Osman548de742020-04-24 12:02:25 -04001550bool SkCanvas::findMarkedCTM(const char* name, SkM44* mx) const {
Brian Osman84aa92e2020-04-30 10:43:33 -04001551 return SkCanvasPriv::ValidateMarker(name) &&
1552 fMarkerStack->findMarker(SkOpts::hash_fn(name, strlen(name), 0), mx);
Mike Reed7fe6ee32020-04-09 12:35:09 -04001553}
1554
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555//////////////////////////////////////////////////////////////////////////////
1556
Mike Reedc1f77742016-12-09 09:00:50 -05001557void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001558 if (!rect.isFinite()) {
1559 return;
1560 }
reed2ff1fce2014-12-11 07:07:37 -08001561 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1563 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001564}
1565
Mike Reedc1f77742016-12-09 09:00:50 -05001566void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001567 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001568
Mike Reed7627fa52017-02-08 10:07:53 -05001569 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001570
reedc64eff52015-11-21 12:39:45 -08001571 AutoValidateClip avc(this);
Brian Osmand8f611d2020-04-21 15:43:47 -04001572 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix.asM33(), this->getTopLayerBounds(),
1573 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001574 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001577void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1578 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001579 if (fClipRestrictionRect.isEmpty()) {
1580 // we notify the device, but we *dont* resolve deferred saves (since we're just
1581 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001582 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001583 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001584 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001585 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001586 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001587 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001588 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1589 }
1590}
1591
Mike Reedc1f77742016-12-09 09:00:50 -05001592void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001593 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001595 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1597 } else {
1598 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001599 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001601
Mike Reedc1f77742016-12-09 09:00:50 -05001602void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001604
Brian Salomona3b45d42016-10-03 11:36:16 -04001605 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001606
Mike Reed7627fa52017-02-08 10:07:53 -05001607 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001608
Brian Osmand8f611d2020-04-21 15:43:47 -04001609 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix.asM33(), this->getTopLayerBounds(),
1610 (SkRegion::Op)op, isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001611 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001612}
1613
Mike Reedc1f77742016-12-09 09:00:50 -05001614void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001615 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001617
Brian Osmand8f611d2020-04-21 15:43:47 -04001618 if (!path.isInverseFillType() && fMCRec->fMatrix.asM33().rectStaysRect()) {
robertphillips39f05382015-11-24 09:30:12 -08001619 SkRect r;
1620 if (path.isRect(&r)) {
1621 this->onClipRect(r, op, edgeStyle);
1622 return;
1623 }
1624 SkRRect rrect;
1625 if (path.isOval(&r)) {
1626 rrect.setOval(r);
1627 this->onClipRRect(rrect, op, edgeStyle);
1628 return;
1629 }
1630 if (path.isRRect(&rrect)) {
1631 this->onClipRRect(rrect, op, edgeStyle);
1632 return;
1633 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 }
robertphillips39f05382015-11-24 09:30:12 -08001635
1636 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637}
1638
Mike Reedc1f77742016-12-09 09:00:50 -05001639void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001640 AutoValidateClip avc(this);
1641
Brian Salomona3b45d42016-10-03 11:36:16 -04001642 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001643
Mike Reed7627fa52017-02-08 10:07:53 -05001644 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645
Brian Salomona3b45d42016-10-03 11:36:16 -04001646 const SkPath* rasterClipPath = &path;
Brian Osmand8f611d2020-04-21 15:43:47 -04001647 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix.asM33(), this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001648 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001649 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650}
1651
Mike Reed121c2af2020-03-10 14:02:56 -04001652void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1653 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001654 if (sh->isOpaque()) {
1655 if (op == SkClipOp::kIntersect) {
1656 // we don't occlude anything, so skip this call
1657 } else {
1658 SkASSERT(op == SkClipOp::kDifference);
1659 // we occlude everything, so set the clip to empty
1660 this->clipRect({0,0,0,0});
1661 }
1662 } else {
1663 this->onClipShader(std::move(sh), op);
1664 }
Mike Reed121c2af2020-03-10 14:02:56 -04001665 }
1666}
1667
1668void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1669 AutoValidateClip avc(this);
1670
1671 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1672
1673 // we don't know how to mutate our conservative bounds, so we don't
1674}
1675
Mike Reedc1f77742016-12-09 09:00:50 -05001676void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001677 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001678 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679}
1680
Mike Reedc1f77742016-12-09 09:00:50 -05001681void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001682 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001683
reed@google.com5c3d1472011-02-22 19:12:23 +00001684 AutoValidateClip avc(this);
1685
Mike Reed20800c82017-11-15 16:09:04 -05001686 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001687 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688}
1689
reed@google.com819c9212011-02-23 18:56:55 +00001690#ifdef SK_DEBUG
1691void SkCanvas::validateClip() const {
1692 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001693 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001694 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001695 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001696 return;
1697 }
reed@google.com819c9212011-02-23 18:56:55 +00001698}
1699#endif
1700
Mike Reeda1361362017-03-07 09:37:29 -05001701bool SkCanvas::androidFramework_isClipAA() const {
1702 bool containsAA = false;
1703
1704 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1705
1706 return containsAA;
1707}
1708
1709class RgnAccumulator {
1710 SkRegion* fRgn;
1711public:
1712 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1713 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1714 SkIPoint origin = device->getOrigin();
1715 if (origin.x() | origin.y()) {
1716 rgn->translate(origin.x(), origin.y());
1717 }
1718 fRgn->op(*rgn, SkRegion::kUnion_Op);
1719 }
1720};
1721
1722void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1723 RgnAccumulator accum(rgn);
1724 SkRegion tmp;
1725
1726 rgn->setEmpty();
1727 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001728}
1729
reed@google.com5c3d1472011-02-22 19:12:23 +00001730///////////////////////////////////////////////////////////////////////////////
1731
reed@google.com754de5f2014-02-24 19:38:20 +00001732bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001733 return fMCRec->fRasterClip.isEmpty();
1734
1735 // TODO: should we only use the conservative answer in a recording canvas?
1736#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001737 SkBaseDevice* dev = this->getTopDevice();
1738 // if no device we return true
1739 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001740#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001741}
1742
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001743bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001744 SkBaseDevice* dev = this->getTopDevice();
1745 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001746 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001747}
1748
msarettfbfa2582016-08-12 08:29:08 -07001749static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1750#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1751 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1752 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1753 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1754 return 0xF != _mm_movemask_ps(mask);
1755#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1756 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1757 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1758 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1759 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1760#else
1761 SkRect devRectAsRect;
1762 SkRect devClipAsRect;
1763 devRect.store(&devRectAsRect.fLeft);
1764 devClip.store(&devClipAsRect.fLeft);
1765 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1766#endif
1767}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001768
msarettfbfa2582016-08-12 08:29:08 -07001769// It's important for this function to not be inlined. Otherwise the compiler will share code
1770// between the fast path and the slow path, resulting in two slow paths.
1771static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1772 const SkMatrix& matrix) {
1773 SkRect deviceRect;
1774 matrix.mapRect(&deviceRect, src);
1775 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1776}
1777
1778bool SkCanvas::quickReject(const SkRect& src) const {
1779#ifdef SK_DEBUG
1780 // Verify that fDeviceClipBounds are set properly.
1781 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001782 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001783 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001784 } else {
msarettfbfa2582016-08-12 08:29:08 -07001785 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 }
msarettfbfa2582016-08-12 08:29:08 -07001787
msarett9637ea92016-08-18 14:03:30 -07001788 // Verify that fIsScaleTranslate is set properly.
Brian Osmand8f611d2020-04-21 15:43:47 -04001789 SkASSERT(fIsScaleTranslate == SkMatrixPriv::IsScaleTranslateAsM33(fMCRec->fMatrix));
msarettfbfa2582016-08-12 08:29:08 -07001790#endif
1791
msarett9637ea92016-08-18 14:03:30 -07001792 if (!fIsScaleTranslate) {
Brian Osmand8f611d2020-04-21 15:43:47 -04001793 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix.asM33());
msarettfbfa2582016-08-12 08:29:08 -07001794 }
1795
1796 // We inline the implementation of mapScaleTranslate() for the fast path.
Brian Osmand8f611d2020-04-21 15:43:47 -04001797 float sx = fMCRec->fMatrix.rc(0, 0);
1798 float sy = fMCRec->fMatrix.rc(1, 1);
1799 float tx = fMCRec->fMatrix.rc(0, 3);
1800 float ty = fMCRec->fMatrix.rc(1, 3);
msarettfbfa2582016-08-12 08:29:08 -07001801 Sk4f scale(sx, sy, sx, sy);
1802 Sk4f trans(tx, ty, tx, ty);
1803
1804 // Apply matrix.
1805 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1806
1807 // Make sure left < right, top < bottom.
1808 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1809 Sk4f min = Sk4f::Min(ltrb, rblt);
1810 Sk4f max = Sk4f::Max(ltrb, rblt);
1811 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1812 // ARM this sequence generates the fastest (a single instruction).
1813 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1814
1815 // Check if the device rect is NaN or outside the clip.
1816 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817}
1818
reed@google.com3b3e8952012-08-16 20:53:31 +00001819bool SkCanvas::quickReject(const SkPath& path) const {
1820 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821}
1822
Mike Klein83c8dd92017-11-28 17:08:45 -05001823SkRect SkCanvas::getLocalClipBounds() const {
1824 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001825 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001826 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 }
1828
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001829 SkMatrix inverse;
1830 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001831 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001832 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001833 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834
Mike Reed42e8c532017-01-23 14:09:13 -05001835 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001836 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001837 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001838
Mike Reedb57b9312018-04-23 12:12:54 -04001839 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001840 inverse.mapRect(&bounds, r);
1841 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842}
1843
Mike Klein83c8dd92017-11-28 17:08:45 -05001844SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001845 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001846}
1847
Mike Reedb18e74d2020-01-16 13:58:22 -05001848///////////////////////////////////////////////////////////////////////
1849
Mike Reed403c8072020-01-08 10:40:39 -05001850SkMatrix SkCanvas::getTotalMatrix() const {
Brian Osmand8f611d2020-04-21 15:43:47 -04001851 return fMCRec->fMatrix.asM33();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852}
1853
Mike Reed46f5c5f2020-02-20 15:42:29 -05001854SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001855 return fMCRec->fMatrix;
1856}
1857
Brian Osman11052242016-10-27 14:47:55 -04001858GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001859 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001860 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001861}
1862
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001863GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001864 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001865 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001866}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001867
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001868void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1869 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001870 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001871 if (outer.isEmpty()) {
1872 return;
1873 }
1874 if (inner.isEmpty()) {
1875 this->drawRRect(outer, paint);
1876 return;
1877 }
1878
1879 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001880 // be able to return ...
1881 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001882 //
1883 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001884 if (!outer.getBounds().contains(inner.getBounds())) {
1885 return;
1886 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001887
1888 this->onDrawDRRect(outer, inner, paint);
1889}
1890
reed41af9662015-01-05 07:49:08 -08001891void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001892 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001893 this->onDrawPaint(paint);
1894}
1895
1896void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001897 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001898 // To avoid redundant logic in our culling code and various backends, we always sort rects
1899 // before passing them along.
1900 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001901}
1902
Mike Reedd5674082019-04-19 15:00:47 -04001903void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1904 TRACE_EVENT0("skia", TRACE_FUNC);
1905 this->onDrawBehind(paint);
1906}
1907
msarettdca352e2016-08-26 06:37:45 -07001908void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001909 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001910 if (region.isEmpty()) {
1911 return;
1912 }
1913
1914 if (region.isRect()) {
1915 return this->drawIRect(region.getBounds(), paint);
1916 }
1917
1918 this->onDrawRegion(region, paint);
1919}
1920
reed41af9662015-01-05 07:49:08 -08001921void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001922 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001923 // To avoid redundant logic in our culling code and various backends, we always sort rects
1924 // before passing them along.
1925 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001926}
1927
1928void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001929 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001930 this->onDrawRRect(rrect, paint);
1931}
1932
1933void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001934 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001935 this->onDrawPoints(mode, count, pts, paint);
1936}
1937
Mike Reede88a1cb2017-03-17 09:50:46 -04001938void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1939 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001940 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001941}
1942
1943void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001944 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001945 RETURN_ON_NULL(vertices);
Brian Osmanf11e3312020-03-24 14:57:38 -04001946
Mike Reed5caf9352020-03-02 14:57:09 -05001947 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Brian Osman8cbedf92020-03-31 10:38:31 -04001948 SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);
Brian Osmanf11e3312020-03-24 14:57:38 -04001949
Brian Osmand1afef62020-04-09 16:24:23 -04001950 // If the vertices contain custom attributes, ensure they line up with the paint's shader.
Brian Osmanf11e3312020-03-24 14:57:38 -04001951 const SkRuntimeEffect* effect =
1952 paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001953 if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) {
Brian Osmanf11e3312020-03-24 14:57:38 -04001954 return;
1955 }
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001956 if (effect) {
1957 int attrIndex = 0;
1958 for (const auto& v : effect->varyings()) {
Brian Osmand1afef62020-04-09 16:24:23 -04001959 const SkVertices::Attribute& attr(vertices->priv().attributes()[attrIndex++]);
1960 // Mismatch between the SkSL varying and the vertex shader output for this attribute
1961 if (attr.channelCount() != v.fWidth) {
1962 return;
1963 }
1964 // If we can't provide any of the asked-for matrices, we can't draw this
Brian Osman548de742020-04-24 12:02:25 -04001965 if (attr.fMarkerID && !fMarkerStack->findMarker(attr.fMarkerID, nullptr)) {
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001966 return;
1967 }
1968 }
1969 }
Brian Osmanf11e3312020-03-24 14:57:38 -04001970
Brian Osman8219e912020-05-15 13:04:48 -04001971#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
1972 // Preserve legacy behavior for Android: ignore the SkShader if there are no texCoords present
1973 if (paint.getShader() &&
1974 !(vertices->priv().hasTexCoords() || vertices->priv().hasCustomData())) {
1975 SkPaint noShaderPaint(paint);
1976 noShaderPaint.setShader(nullptr);
1977 this->onDrawVerticesObject(vertices, mode, noShaderPaint);
1978 return;
1979 }
1980#endif
1981
Mike Reed5caf9352020-03-02 14:57:09 -05001982 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001983}
1984
1985void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001986 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001987 this->onDrawPath(path, paint);
1988}
1989
reeda85d4d02015-05-06 12:56:48 -07001990void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001991 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001992 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001993 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001994}
1995
Mike Reedc4e31092018-01-30 11:15:27 -05001996// Returns true if the rect can be "filled" : non-empty and finite
1997static bool fillable(const SkRect& r) {
1998 SkScalar w = r.width();
1999 SkScalar h = r.height();
2000 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
2001}
2002
reede47829b2015-08-06 10:02:53 -07002003void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2004 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002005 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002006 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002007 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002008 return;
2009 }
2010 this->onDrawImageRect(image, &src, dst, paint, constraint);
2011}
reed41af9662015-01-05 07:49:08 -08002012
reed84984ef2015-07-17 07:09:43 -07002013void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2014 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002015 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002016 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002017}
2018
Brian Salomonf08002c2018-10-26 16:15:46 -04002019void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002020 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002021 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002022 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002023}
reede47829b2015-08-06 10:02:53 -07002024
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002025namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002026class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002027public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002028 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2029 if (!origPaint) {
2030 return;
2031 }
2032 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2033 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2034 }
2035 if (origPaint->getMaskFilter()) {
2036 fPaint.writable()->setMaskFilter(nullptr);
2037 }
2038 if (origPaint->isAntiAlias()) {
2039 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002040 }
2041 }
2042
2043 const SkPaint* get() const {
2044 return fPaint;
2045 }
2046
2047private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002048 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002049};
2050} // namespace
2051
reed4c21dc52015-06-25 12:32:03 -07002052void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2053 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002054 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002055 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002056 if (dst.isEmpty()) {
2057 return;
2058 }
msarett552bca92016-08-03 06:53:26 -07002059 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002060 LatticePaint latticePaint(paint);
2061 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002062 } else {
reede47829b2015-08-06 10:02:53 -07002063 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002064 }
reed4c21dc52015-06-25 12:32:03 -07002065}
2066
msarett16882062016-08-16 09:31:08 -07002067void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2068 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002069 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002070 RETURN_ON_NULL(image);
2071 if (dst.isEmpty()) {
2072 return;
2073 }
msarett71df2d72016-09-30 12:41:42 -07002074
2075 SkIRect bounds;
2076 Lattice latticePlusBounds = lattice;
2077 if (!latticePlusBounds.fBounds) {
2078 bounds = SkIRect::MakeWH(image->width(), image->height());
2079 latticePlusBounds.fBounds = &bounds;
2080 }
2081
2082 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002083 LatticePaint latticePaint(paint);
2084 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002085 } else {
2086 this->drawImageRect(image, dst, paint);
2087 }
2088}
2089
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002090static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002091 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002092 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002093 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002094 return SkImage::MakeFromBitmap(bitmap);
2095}
2096
2097void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2098 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002099}
2100
reede47829b2015-08-06 10:02:53 -07002101void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002102 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002103 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002104}
2105
reed84984ef2015-07-17 07:09:43 -07002106void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2107 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002108 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002109}
2110
reede47829b2015-08-06 10:02:53 -07002111void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2112 SrcRectConstraint constraint) {
2113 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2114 constraint);
2115}
reede47829b2015-08-06 10:02:53 -07002116
reed71c3c762015-06-24 10:29:17 -07002117void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002118 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002119 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002120 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002121 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002122 if (count <= 0) {
2123 return;
2124 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002125 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002126 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002127 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002128}
2129
reedf70b5312016-03-04 16:36:20 -08002130void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002131 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002132 if (key) {
2133 this->onDrawAnnotation(rect, key, value);
2134 }
2135}
2136
reede47829b2015-08-06 10:02:53 -07002137void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2138 const SkPaint* paint, SrcRectConstraint constraint) {
2139 if (src) {
2140 this->drawImageRect(image, *src, dst, paint, constraint);
2141 } else {
2142 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2143 dst, paint, constraint);
2144 }
2145}
reede47829b2015-08-06 10:02:53 -07002146
Mike Reed4204da22017-05-17 08:53:36 -04002147void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002148 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002149 this->onDrawShadowRec(path, rec);
2150}
2151
2152void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2153 SkPaint paint;
2154 const SkRect& pathBounds = path.getBounds();
2155
Mike Reed38992392019-07-30 10:48:15 -04002156 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002157 while (iter.next()) {
2158 iter.fDevice->drawShadow(path, rec);
2159 }
Mike Reed38992392019-07-30 10:48:15 -04002160 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002161}
2162
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002163void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002164 QuadAAFlags aaFlags, const SkColor4f& color,
2165 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002166 TRACE_EVENT0("skia", TRACE_FUNC);
2167 // Make sure the rect is sorted before passing it along
2168 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2169}
2170
2171void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2172 const SkPoint dstClips[],
2173 const SkMatrix preViewMatrices[],
2174 const SkPaint* paint,
2175 SrcRectConstraint constraint) {
2176 TRACE_EVENT0("skia", TRACE_FUNC);
2177 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2178}
2179
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180//////////////////////////////////////////////////////////////////////////////
2181// These are the virtual drawing methods
2182//////////////////////////////////////////////////////////////////////////////
2183
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002184void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002185 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002186 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2187 }
2188}
2189
reed41af9662015-01-05 07:49:08 -08002190void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002191 this->internalDrawPaint(paint);
2192}
2193
2194void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002195 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196
2197 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002198 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199 }
2200
Mike Reed38992392019-07-30 10:48:15 -04002201 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202}
2203
reed41af9662015-01-05 07:49:08 -08002204void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2205 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 if ((long)count <= 0) {
2207 return;
2208 }
2209
Mike Reed822128b2017-02-28 16:41:03 -05002210 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002211 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002212 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002213 // special-case 2 points (common for drawing a single line)
2214 if (2 == count) {
2215 r.set(pts[0], pts[1]);
2216 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002217 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002218 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002219 if (!r.isFinite()) {
2220 return;
2221 }
Mike Reed822128b2017-02-28 16:41:03 -05002222 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002223 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2224 return;
2225 }
2226 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002227 }
reed@google.coma584aed2012-05-16 14:06:02 +00002228
halcanary96fcdcc2015-08-27 07:41:13 -07002229 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230
Mike Reed38992392019-07-30 10:48:15 -04002231 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002232
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002234 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 }
reed@google.com4b226022011-01-11 18:32:13 +00002236
Mike Reed38992392019-07-30 10:48:15 -04002237 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238}
2239
reed4a167172016-08-18 17:15:25 -07002240static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002241 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002242}
2243
reed41af9662015-01-05 07:49:08 -08002244void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002245 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002247 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002248 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002249 return;
2250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251 }
reed@google.com4b226022011-01-11 18:32:13 +00002252
reed4a167172016-08-18 17:15:25 -07002253 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002254 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255
reed4a167172016-08-18 17:15:25 -07002256 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002257 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002258 }
2259
Mike Reed38992392019-07-30 10:48:15 -04002260 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002261 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002262 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002263 SkDrawIter iter(this);
2264 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002265 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002266 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268}
2269
msarett44df6512016-08-25 13:54:30 -07002270void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002271 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002272 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002273 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002274 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2275 return;
2276 }
msarett44df6512016-08-25 13:54:30 -07002277 }
2278
Mike Reed38992392019-07-30 10:48:15 -04002279 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002280
2281 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002282 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002283 }
2284
Mike Reed38992392019-07-30 10:48:15 -04002285 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002286}
2287
Mike Reedd5674082019-04-19 15:00:47 -04002288void SkCanvas::onDrawBehind(const SkPaint& paint) {
2289 SkIRect bounds;
2290 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2291 for (;;) {
2292 const MCRec* rec = (const MCRec*)iter.prev();
2293 if (!rec) {
2294 return; // no backimages, so nothing to draw
2295 }
2296 if (rec->fBackImage) {
2297 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2298 rec->fBackImage->fImage->width(),
2299 rec->fBackImage->fImage->height());
2300 break;
2301 }
2302 }
2303
Mike Reed38992392019-07-30 10:48:15 -04002304 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002305
2306 while (iter.next()) {
2307 SkBaseDevice* dev = iter.fDevice;
2308
Mike Reedd5674082019-04-19 15:00:47 -04002309 dev->save();
2310 // We use clipRegion because it is already defined to operate in dev-space
2311 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2312 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002313 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002314 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002315 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002316 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002317 }
2318
Mike Reed38992392019-07-30 10:48:15 -04002319 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002320}
2321
reed41af9662015-01-05 07:49:08 -08002322void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002323 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002324 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002325 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002326 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002327 return;
2328 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002329 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002330
Mike Reed38992392019-07-30 10:48:15 -04002331 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002332
2333 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002334 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002335 }
2336
Mike Reed38992392019-07-30 10:48:15 -04002337 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002338}
2339
bsalomonac3aa242016-08-19 11:25:19 -07002340void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2341 SkScalar sweepAngle, bool useCenter,
2342 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002343 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002344 if (paint.canComputeFastBounds()) {
2345 SkRect storage;
2346 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002347 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002348 return;
2349 }
bsalomonac3aa242016-08-19 11:25:19 -07002350 }
2351
Mike Reed38992392019-07-30 10:48:15 -04002352 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002353
2354 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002355 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002356 }
2357
Mike Reed38992392019-07-30 10:48:15 -04002358 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002359}
2360
reed41af9662015-01-05 07:49:08 -08002361void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002362 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002363 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002364 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2365 return;
2366 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002367 }
2368
2369 if (rrect.isRect()) {
2370 // call the non-virtual version
2371 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002372 return;
2373 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002374 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002375 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2376 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002377 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002378
Mike Reed38992392019-07-30 10:48:15 -04002379 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002380
2381 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002382 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002383 }
2384
Mike Reed38992392019-07-30 10:48:15 -04002385 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002386}
2387
Mike Reed822128b2017-02-28 16:41:03 -05002388void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002389 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002390 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002391 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2392 return;
2393 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002394 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002395
Mike Reed38992392019-07-30 10:48:15 -04002396 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002397
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002398 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002399 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002400 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002401
Mike Reed38992392019-07-30 10:48:15 -04002402 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002403}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002404
reed41af9662015-01-05 07:49:08 -08002405void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002406 if (!path.isFinite()) {
2407 return;
2408 }
2409
Mike Reed822128b2017-02-28 16:41:03 -05002410 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002411 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002412 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002413 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2414 return;
2415 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002417
Mike Reed822128b2017-02-28 16:41:03 -05002418 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002419 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002420 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002421 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002422 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002423 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002424
Mike Reed38992392019-07-30 10:48:15 -04002425 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426
2427 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002428 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002429 }
2430
Mike Reed38992392019-07-30 10:48:15 -04002431 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432}
2433
reed262a71b2015-12-05 13:07:27 -08002434bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002435 if (!paint.getImageFilter()) {
2436 return false;
2437 }
2438
2439 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002440 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002441 return false;
2442 }
2443
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002444 // The other paint effects need to be applied before the image filter, but the sprite draw
2445 // applies the filter explicitly first.
2446 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2447 return false;
2448 }
reed262a71b2015-12-05 13:07:27 -08002449 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2450 // Once we can filter and the filter will return a result larger than itself, we should be
2451 // able to remove this constraint.
2452 // skbug.com/4526
2453 //
2454 SkPoint pt;
2455 ctm.mapXY(x, y, &pt);
2456 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2457 return ir.contains(fMCRec->fRasterClip.getBounds());
2458}
2459
Mike Reedf441cfc2018-04-11 14:50:16 -04002460// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2461// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2462// null.
2463static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2464 if (paintParam) {
2465 *real = *paintParam;
2466 real->setStyle(SkPaint::kFill_Style);
2467 real->setPathEffect(nullptr);
2468 paintParam = real;
2469 }
2470 return paintParam;
2471}
2472
reeda85d4d02015-05-06 12:56:48 -07002473void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002474 SkPaint realPaint;
2475 paint = init_image_paint(&realPaint, paint);
2476
reeda85d4d02015-05-06 12:56:48 -07002477 SkRect bounds = SkRect::MakeXYWH(x, y,
2478 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002479 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002480 SkRect tmp = bounds;
2481 if (paint) {
2482 paint->computeFastBounds(tmp, &tmp);
2483 }
2484 if (this->quickReject(tmp)) {
2485 return;
2486 }
reeda85d4d02015-05-06 12:56:48 -07002487 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002488 // At this point we need a real paint object. If the caller passed null, then we should
2489 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2490 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2491 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002492
reeda2217ef2016-07-20 06:04:34 -07002493 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002494 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2495 *paint);
2496 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002497 special = this->getDevice()->makeSpecial(image);
2498 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002499 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002500 }
2501 }
2502
Mike Reed38992392019-07-30 10:48:15 -04002503 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002504
reeda85d4d02015-05-06 12:56:48 -07002505 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002506 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002507 if (special) {
2508 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002509 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002510 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002511 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002512 SkScalarRoundToInt(pt.fY), pnt,
2513 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002514 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002515 iter.fDevice->drawImageRect(
2516 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2517 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002518 }
reeda85d4d02015-05-06 12:56:48 -07002519 }
halcanary9d524f22016-03-29 09:03:52 -07002520
Mike Reed38992392019-07-30 10:48:15 -04002521 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002522}
2523
reed41af9662015-01-05 07:49:08 -08002524void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002525 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002526 SkPaint realPaint;
2527 paint = init_image_paint(&realPaint, paint);
2528
halcanary96fcdcc2015-08-27 07:41:13 -07002529 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002530 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002531 if (paint) {
2532 paint->computeFastBounds(dst, &storage);
2533 }
2534 if (this->quickReject(storage)) {
2535 return;
2536 }
reeda85d4d02015-05-06 12:56:48 -07002537 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002538 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002539
Mike Reed38992392019-07-30 10:48:15 -04002540 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002541
reeda85d4d02015-05-06 12:56:48 -07002542 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002543 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002544 }
halcanary9d524f22016-03-29 09:03:52 -07002545
Mike Reed38992392019-07-30 10:48:15 -04002546 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002547}
2548
reed4c21dc52015-06-25 12:32:03 -07002549void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2550 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002551 SkPaint realPaint;
2552 paint = init_image_paint(&realPaint, paint);
2553
halcanary96fcdcc2015-08-27 07:41:13 -07002554 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002555 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002556 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2557 return;
2558 }
reed@google.com3d608122011-11-21 15:16:16 +00002559 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002560 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002561
Mike Reed38992392019-07-30 10:48:15 -04002562 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002563
reed4c21dc52015-06-25 12:32:03 -07002564 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002565 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002566 }
halcanary9d524f22016-03-29 09:03:52 -07002567
Mike Reed38992392019-07-30 10:48:15 -04002568 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002569}
2570
msarett16882062016-08-16 09:31:08 -07002571void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2572 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002573 SkPaint realPaint;
2574 paint = init_image_paint(&realPaint, paint);
2575
msarett16882062016-08-16 09:31:08 -07002576 if (nullptr == paint || paint->canComputeFastBounds()) {
2577 SkRect storage;
2578 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2579 return;
2580 }
2581 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002582 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002583
Mike Reed38992392019-07-30 10:48:15 -04002584 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002585
2586 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002587 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002588 }
2589
Mike Reed38992392019-07-30 10:48:15 -04002590 DRAW_END
msarett16882062016-08-16 09:31:08 -07002591}
2592
fmalita00d5c2c2014-08-21 08:53:26 -07002593void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2594 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002595 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002596 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002597 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002598 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002599 SkRect tmp;
2600 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2601 return;
2602 }
2603 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002604 }
2605
fmalita024f9962015-03-03 19:08:17 -08002606 // We cannot filter in the looper as we normally do, because the paint is
2607 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002608 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002609
fmalitaaa1b9122014-08-28 14:32:24 -07002610 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002611 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002612 }
2613
Mike Reed38992392019-07-30 10:48:15 -04002614 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002615}
2616
Mike Reed358fcad2018-11-23 15:27:51 -05002617// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002618void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002619 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2620 TRACE_EVENT0("skia", TRACE_FUNC);
2621 if (byteLength) {
2622 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002623 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002624 }
2625}
Mike Reed4f81bb72019-01-23 09:23:00 -05002626
fmalita00d5c2c2014-08-21 08:53:26 -07002627void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2628 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002629 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002630 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002631 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002632 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002633}
reed@google.come0d9ce82014-04-23 04:00:17 +00002634
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002635void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2636 const SkPaint& paint) {
2637 DRAW_BEGIN(paint, nullptr)
2638
2639 while (iter.next()) {
2640 // In the common case of one iteration we could std::move vertices here.
2641 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2642 }
2643
2644 DRAW_END
2645}
Brian Salomon199fb872017-02-06 09:41:10 -05002646
dandovb3c9d1c2014-08-12 08:34:29 -07002647void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002648 const SkPoint texCoords[4], SkBlendMode bmode,
2649 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002650 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002651 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002652 return;
2653 }
mtklein6cfa73a2014-08-13 13:33:49 -07002654
Mike Reedfaba3712016-11-03 14:45:31 -04002655 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002656}
2657
2658void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002659 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002660 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002661 // Since a patch is always within the convex hull of the control points, we discard it when its
2662 // bounding rectangle is completely outside the current clip.
2663 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002664 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002665 if (this->quickReject(bounds)) {
2666 return;
2667 }
mtklein6cfa73a2014-08-13 13:33:49 -07002668
Mike Reed38992392019-07-30 10:48:15 -04002669 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002670
dandovecfff212014-08-04 10:02:00 -07002671 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002672 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002673 }
mtklein6cfa73a2014-08-13 13:33:49 -07002674
Mike Reed38992392019-07-30 10:48:15 -04002675 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002676}
2677
reeda8db7282015-07-07 10:22:31 -07002678void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002679#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002680 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002681#endif
reede3b38ce2016-01-08 09:18:44 -08002682 RETURN_ON_NULL(dr);
2683 if (x || y) {
Mike Reed1f607332020-05-21 12:11:27 -04002684 SkMatrix matrix = SkMatrix::Translate(x, y);
reede3b38ce2016-01-08 09:18:44 -08002685 this->onDrawDrawable(dr, &matrix);
2686 } else {
2687 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002688 }
2689}
2690
reeda8db7282015-07-07 10:22:31 -07002691void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002692#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002693 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002694#endif
reede3b38ce2016-01-08 09:18:44 -08002695 RETURN_ON_NULL(dr);
2696 if (matrix && matrix->isIdentity()) {
2697 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002698 }
reede3b38ce2016-01-08 09:18:44 -08002699 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002700}
2701
2702void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002703 // drawable bounds are no longer reliable (e.g. android displaylist)
2704 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002705 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002706}
2707
reed71c3c762015-06-24 10:29:17 -07002708void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002709 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002710 const SkRect* cull, const SkPaint* paint) {
2711 if (cull && this->quickReject(*cull)) {
2712 return;
2713 }
2714
2715 SkPaint pnt;
2716 if (paint) {
2717 pnt = *paint;
2718 }
halcanary9d524f22016-03-29 09:03:52 -07002719
Mike Reed38992392019-07-30 10:48:15 -04002720 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002721 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002722 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002723 }
Mike Reed38992392019-07-30 10:48:15 -04002724 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002725}
2726
reedf70b5312016-03-04 16:36:20 -08002727void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2728 SkASSERT(key);
2729
2730 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002731 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002732 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002733 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002734 }
Mike Reed38992392019-07-30 10:48:15 -04002735 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002736}
2737
Michael Ludwiga595f862019-08-27 15:25:49 -04002738void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2739 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002740 SkASSERT(r.isSorted());
2741
2742 // If this used a paint, it would be a filled color with blend mode, which does not
2743 // need to use an autodraw loop, so use SkDrawIter directly.
2744 if (this->quickReject(r)) {
2745 return;
2746 }
2747
Michael Ludwiga4b44882019-08-28 14:34:58 -04002748 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002749 SkDrawIter iter(this);
2750 while(iter.next()) {
2751 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2752 }
2753}
2754
2755void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2756 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2757 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002758 if (count <= 0) {
2759 // Nothing to draw
2760 return;
2761 }
2762
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002763 SkPaint realPaint;
2764 init_image_paint(&realPaint, paint);
2765
Michael Ludwiga4b44882019-08-28 14:34:58 -04002766 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2767 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2768 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2769 // or we need it for the autolooper (since it greatly improves image filter perf).
2770 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2771 bool setBoundsValid = count == 1 || needsAutoLooper;
2772 SkRect setBounds = imageSet[0].fDstRect;
2773 if (imageSet[0].fMatrixIndex >= 0) {
2774 // Account for the per-entry transform that is applied prior to the CTM when drawing
2775 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002776 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002777 if (needsAutoLooper) {
2778 for (int i = 1; i < count; ++i) {
2779 SkRect entryBounds = imageSet[i].fDstRect;
2780 if (imageSet[i].fMatrixIndex >= 0) {
2781 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2782 }
2783 setBounds.joinPossiblyEmptyRect(entryBounds);
2784 }
2785 }
2786
2787 // If we happen to have the draw bounds, though, might as well check quickReject().
2788 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2789 SkRect tmp;
2790 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2791 return;
2792 }
2793 }
2794
2795 if (needsAutoLooper) {
2796 SkASSERT(setBoundsValid);
2797 DRAW_BEGIN(realPaint, &setBounds)
2798 while (iter.next()) {
2799 iter.fDevice->drawEdgeAAImageSet(
2800 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2801 }
2802 DRAW_END
2803 } else {
2804 this->predrawNotify();
2805 SkDrawIter iter(this);
2806 while(iter.next()) {
2807 iter.fDevice->drawEdgeAAImageSet(
2808 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2809 }
2810 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002811}
2812
reed@android.com8a1c16f2008-12-17 15:59:43 +00002813//////////////////////////////////////////////////////////////////////////////
2814// These methods are NOT virtual, and therefore must call back into virtual
2815// methods, rather than actually drawing themselves.
2816//////////////////////////////////////////////////////////////////////////////
2817
reed374772b2016-10-05 17:33:02 -07002818void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002819 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002820 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002821 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002822 this->drawPaint(paint);
2823}
2824
2825void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002826 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002827 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2828}
2829
Mike Reed3661bc92017-02-22 13:21:42 -05002830void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002832 pts[0].set(x0, y0);
2833 pts[1].set(x1, y1);
2834 this->drawPoints(kLines_PointMode, 2, pts, paint);
2835}
2836
Mike Reed3661bc92017-02-22 13:21:42 -05002837void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002838 if (radius < 0) {
2839 radius = 0;
2840 }
2841
2842 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002843 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002844 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002845}
2846
2847void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2848 const SkPaint& paint) {
2849 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002850 SkRRect rrect;
2851 rrect.setRectXY(r, rx, ry);
2852 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002853 } else {
2854 this->drawRect(r, paint);
2855 }
2856}
2857
reed@android.com8a1c16f2008-12-17 15:59:43 +00002858void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2859 SkScalar sweepAngle, bool useCenter,
2860 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002861 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002862 if (oval.isEmpty() || !sweepAngle) {
2863 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002864 }
bsalomon21af9ca2016-08-25 12:29:23 -07002865 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002866}
2867
reed@android.comf76bacf2009-05-13 14:00:33 +00002868///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002869#ifdef SK_DISABLE_SKPICTURE
2870void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002871
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002872
2873void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2874 const SkPaint* paint) {}
2875#else
Mike Klein88d90712018-01-27 17:30:04 +00002876/**
2877 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2878 * against the playback cost of recursing into the subpicture to get at its actual ops.
2879 *
2880 * For now we pick a conservatively small value, though measurement (and other heuristics like
2881 * the type of ops contained) may justify changing this value.
2882 */
2883#define kMaxPictureOpsToUnrollInsteadOfRef 1
2884
reedd5fa1a42014-08-09 11:08:05 -07002885void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002886 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002887 RETURN_ON_NULL(picture);
2888
reede3b38ce2016-01-08 09:18:44 -08002889 if (matrix && matrix->isIdentity()) {
2890 matrix = nullptr;
2891 }
Mike Klein88d90712018-01-27 17:30:04 +00002892 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2893 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2894 picture->playback(this);
2895 } else {
2896 this->onDrawPicture(picture, matrix, paint);
2897 }
reedd5fa1a42014-08-09 11:08:05 -07002898}
robertphillips9b14f262014-06-04 05:40:44 -07002899
reedd5fa1a42014-08-09 11:08:05 -07002900void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2901 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002902 if (!paint || paint->canComputeFastBounds()) {
2903 SkRect bounds = picture->cullRect();
2904 if (paint) {
2905 paint->computeFastBounds(bounds, &bounds);
2906 }
2907 if (matrix) {
2908 matrix->mapRect(&bounds);
2909 }
2910 if (this->quickReject(bounds)) {
2911 return;
2912 }
2913 }
2914
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002915 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002916 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002917}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002918#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002919
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920///////////////////////////////////////////////////////////////////////////////
2921///////////////////////////////////////////////////////////////////////////////
2922
reed3aafe112016-08-18 12:45:34 -07002923SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002924 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002925
2926 SkASSERT(canvas);
2927
reed3aafe112016-08-18 12:45:34 -07002928 fImpl = new (fStorage) SkDrawIter(canvas);
Michael Ludwig2a8a3ff2020-03-26 15:33:20 -04002929 // This advances the base iterator to the first device and caches its origin,
2930 // correctly handling the case where there are no devices.
2931 this->next();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002932}
2933
2934SkCanvas::LayerIter::~LayerIter() {
2935 fImpl->~SkDrawIter();
2936}
2937
2938void SkCanvas::LayerIter::next() {
2939 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002940 if (!fDone) {
2941 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2942 // filters, so its devices will always be able to report the origin exactly.
2943 fDeviceOrigin = fImpl->fDevice->getOrigin();
2944 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002945}
2946
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002947SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002948 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949}
2950
2951const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002952 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953}
2954
2955const SkPaint& SkCanvas::LayerIter::paint() const {
2956 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002957 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958 paint = &fDefaultPaint;
2959 }
2960 return *paint;
2961}
2962
Mike Reedca37f322018-03-08 13:22:16 -05002963SkIRect SkCanvas::LayerIter::clipBounds() const {
2964 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002965}
2966
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002967int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2968int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002969
2970///////////////////////////////////////////////////////////////////////////////
2971
Brian Osmane8a98632019-04-10 10:26:10 -04002972SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2973SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2974SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2975SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
2976
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002977SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2978 const SkRect& dstRect, int matrixIndex, float alpha,
2979 unsigned aaFlags, bool hasClip)
2980 : fImage(std::move(image))
2981 , fSrcRect(srcRect)
2982 , fDstRect(dstRect)
2983 , fMatrixIndex(matrixIndex)
2984 , fAlpha(alpha)
2985 , fAAFlags(aaFlags)
2986 , fHasClip(hasClip) {}
2987
2988SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2989 const SkRect& dstRect, float alpha, unsigned aaFlags)
2990 : fImage(std::move(image))
2991 , fSrcRect(srcRect)
2992 , fDstRect(dstRect)
2993 , fAlpha(alpha)
2994 , fAAFlags(aaFlags) {}
2995
2996///////////////////////////////////////////////////////////////////////////////
2997
Mike Reed5df49342016-11-12 08:06:55 -06002998std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05002999 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003000 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003001 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003002 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003003
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003004 SkBitmap bitmap;
3005 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003006 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003007 }
Mike Reed12f77342017-11-08 11:19:52 -05003008
3009 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003010 std::make_unique<SkCanvas>(bitmap, *props) :
3011 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003012}
reedd5fa1a42014-08-09 11:08:05 -07003013
3014///////////////////////////////////////////////////////////////////////////////
3015
Florin Malitaee424ac2016-12-01 12:47:59 -05003016SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003017 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003018
Florin Malita439ace92016-12-02 12:05:41 -05003019SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003020 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003021
Herb Derbyefe39bc2018-05-01 17:06:20 -04003022SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003023 : INHERITED(device) {}
3024
Florin Malitaee424ac2016-12-01 12:47:59 -05003025SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3026 (void)this->INHERITED::getSaveLayerStrategy(rec);
3027 return kNoLayer_SaveLayerStrategy;
3028}
3029
Mike Reed148b7fd2018-12-18 17:38:18 -05003030bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3031 return false;
3032}
3033
Florin Malitaee424ac2016-12-01 12:47:59 -05003034///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003035
reed73603f32016-09-20 08:42:38 -07003036static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3037static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3038static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3039static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3040static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3041static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003042
3043///////////////////////////////////////////////////////////////////////////////////////////////////
3044
3045SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3046 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003047 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003048 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003049 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003050 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003051 clip.setEmpty();
3052 }
3053
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003054 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003055 return handle;
3056 }
3057 return nullptr;
3058}
3059
3060static bool install(SkBitmap* bm, const SkImageInfo& info,
3061 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003062 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003063}
3064
3065SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3066 SkBitmap* bm) {
3067 SkRasterHandleAllocator::Rec rec;
3068 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3069 return nullptr;
3070 }
3071 return rec.fHandle;
3072}
3073
3074std::unique_ptr<SkCanvas>
3075SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3076 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003077 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003078 return nullptr;
3079 }
3080
3081 SkBitmap bm;
3082 Handle hndl;
3083
3084 if (rec) {
3085 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3086 } else {
3087 hndl = alloc->allocBitmap(info, &bm);
3088 }
3089 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3090}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003091
3092///////////////////////////////////////////////////////////////////////////////////////////////////