blob: b3f4a165cba4d36c19e5fd686b704ef245eae69d [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Brian Osmanf11e3312020-03-24 14:57:38 -040020#include "include/effects/SkRuntimeEffect.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/private/SkNx.h"
22#include "include/private/SkTo.h"
23#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040024#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050026#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/core/SkCanvasPriv.h"
28#include "src/core/SkClipOpPriv.h"
29#include "src/core/SkClipStack.h"
30#include "src/core/SkDraw.h"
31#include "src/core/SkGlyphRun.h"
32#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040033#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050034#include "src/core/SkLatticeIter.h"
35#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050036#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "src/core/SkMatrixUtils.h"
38#include "src/core/SkPaintPriv.h"
39#include "src/core/SkRasterClip.h"
40#include "src/core/SkSpecialImage.h"
41#include "src/core/SkStrikeCache.h"
42#include "src/core/SkTLazy.h"
43#include "src/core/SkTextFormatParams.h"
44#include "src/core/SkTraceEvent.h"
Mike Reedba962562020-03-12 20:33:21 -040045#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050046#include "src/image/SkImage_Base.h"
47#include "src/image/SkSurface_Base.h"
48#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040049
bungemand3ebb482015-08-05 13:57:49 -070050#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050053#include "include/gpu/GrContext.h"
54#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000055#endif
56
reede3b38ce2016-01-08 09:18:44 -080057#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050058#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080059
Mike Klein1bb7e232019-12-10 08:58:52 -060060// This is a test: static_assert with no message is a c++17 feature,
61// and std::max() is constexpr only since the c++14 stdlib.
62static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000063
Mike Reed139e5e02017-03-08 11:29:33 -050064///////////////////////////////////////////////////////////////////////////////////////////////////
65
reedc83a2972015-07-16 07:40:45 -070066/*
67 * Return true if the drawing this rect would hit every pixels in the canvas.
68 *
69 * Returns false if
70 * - rect does not contain the canvas' bounds
71 * - paint is not fill
72 * - paint would blur or otherwise change the coverage of the rect
73 */
74bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
75 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070076 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
77 (int)kNone_ShaderOverrideOpacity,
78 "need_matching_enums0");
79 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
80 (int)kOpaque_ShaderOverrideOpacity,
81 "need_matching_enums1");
82 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
83 (int)kNotOpaque_ShaderOverrideOpacity,
84 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070085
86 const SkISize size = this->getBaseLayerSize();
87 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050088
89 // if we're clipped at all, we can't overwrite the entire surface
90 {
91 SkBaseDevice* base = this->getDevice();
92 SkBaseDevice* top = this->getTopDevice();
93 if (base != top) {
94 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
95 }
96 if (!base->clipIsWideOpen()) {
97 return false;
98 }
reedc83a2972015-07-16 07:40:45 -070099 }
100
101 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700102 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700103 return false; // conservative
104 }
halcanaryc5769b22016-08-10 07:13:21 -0700105
106 SkRect devRect;
107 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
108 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700109 return false;
110 }
111 }
112
113 if (paint) {
114 SkPaint::Style paintStyle = paint->getStyle();
115 if (!(paintStyle == SkPaint::kFill_Style ||
116 paintStyle == SkPaint::kStrokeAndFill_Style)) {
117 return false;
118 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400119 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700120 return false; // conservative
121 }
122 }
123 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
124}
125
126///////////////////////////////////////////////////////////////////////////////////////////////////
127
reed@google.comda17f752012-08-16 18:27:05 +0000128// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129//#define SK_TRACE_SAVERESTORE
130
131#ifdef SK_TRACE_SAVERESTORE
132 static int gLayerCounter;
133 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
134 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
135
136 static int gRecCounter;
137 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
138 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
139
140 static int gCanvasCounter;
141 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
142 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
143#else
144 #define inc_layer()
145 #define dec_layer()
146 #define inc_rec()
147 #define dec_rec()
148 #define inc_canvas()
149 #define dec_canvas()
150#endif
151
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000152typedef SkTLazy<SkPaint> SkLazyPaint;
153
reedc83a2972015-07-16 07:40:45 -0700154void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000155 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700156 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
157 ? SkSurface::kDiscard_ContentChangeMode
158 : SkSurface::kRetain_ContentChangeMode);
159 }
160}
161
162void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
163 ShaderOverrideOpacity overrideOpacity) {
164 if (fSurfaceBase) {
165 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
166 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
167 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
168 // and therefore we don't care which mode we're in.
169 //
170 if (fSurfaceBase->outstandingImageSnapshot()) {
171 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
172 mode = SkSurface::kDiscard_ContentChangeMode;
173 }
174 }
175 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000176 }
177}
178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000181/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 The clip/matrix/proc are fields that reflect the top of the save/restore
183 stack. Whenever the canvas changes, it marks a dirty flag, and then before
184 these are used (assuming we're not on a layer) we rebuild these cache
185 values: they reflect the top of the save stack, but translated and clipped
186 by the device's XY offset and bitmap-bounds.
187*/
188struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400189 DeviceCM* fNext;
190 sk_sp<SkBaseDevice> fDevice;
191 SkRasterClip fClip;
192 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
193 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400194 sk_sp<SkImage> fClipImage;
195 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196
Florin Malita53f77bd2017-04-28 13:48:37 -0400197 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000198 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700199 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400200 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500201 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700202 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000203 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400204 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400205 {}
reed@google.com4b226022011-01-11 18:32:13 +0000206
mtkleinfeaadee2015-04-08 11:25:48 -0700207 void reset(const SkIRect& bounds) {
208 SkASSERT(!fPaint);
209 SkASSERT(!fNext);
210 SkASSERT(fDevice);
211 fClip.setRect(bounds);
212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213};
214
Mike Reed148b7fd2018-12-18 17:38:18 -0500215namespace {
216// Encapsulate state needed to restore from saveBehind()
217struct BackImage {
218 sk_sp<SkSpecialImage> fImage;
219 SkIPoint fLoc;
220};
221}
222
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223/* This is the record we keep for each save/restore level in the stack.
224 Since a level optionally copies the matrix and/or stack, we have pointers
225 for these fields. If the value is copied for this level, the copy is
226 stored in the ...Storage field, and the pointer points to that. If the
227 value is not copied for this level, we ignore ...Storage, and just point
228 at the corresponding value in the previous level in the stack.
229*/
230class SkCanvas::MCRec {
231public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500232 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 /* If there are any layers in the stack, this points to the top-most
234 one that is at or below this level in the stack (so we know what
235 bitmap/device to draw into from this level. This value is NOT
236 reference counted, since the real owner is either our fLayer field,
237 or a previous one in a lower level.)
238 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500239 DeviceCM* fTopLayer;
240 std::unique_ptr<BackImage> fBackImage;
241 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500242 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500243 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
Mike Reeda1361362017-03-07 09:37:29 -0500245 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700246 fLayer = nullptr;
247 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800248 fMatrix.reset();
249 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700250
reedd9544982014-09-09 18:46:22 -0700251 // don't bother initializing fNext
252 inc_rec();
253 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400254 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700255 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700256 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800257 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 // don't bother initializing fNext
260 inc_rec();
261 }
262 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700263 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 dec_rec();
265 }
mtkleinfeaadee2015-04-08 11:25:48 -0700266
267 void reset(const SkIRect& bounds) {
268 SkASSERT(fLayer);
269 SkASSERT(fDeferredSaveCount == 0);
270
271 fMatrix.reset();
272 fRasterClip.setRect(bounds);
273 fLayer->reset(bounds);
274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275};
276
Mike Reeda1361362017-03-07 09:37:29 -0500277class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278public:
Mike Reeda1361362017-03-07 09:37:29 -0500279 SkDrawIter(SkCanvas* canvas)
280 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
281 {}
reed@google.com4b226022011-01-11 18:32:13 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000284 const DeviceCM* rec = fCurrLayer;
285 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400286 fDevice = rec->fDevice.get();
287 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700289 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 return true;
291 }
292 return false;
293 }
reed@google.com4b226022011-01-11 18:32:13 +0000294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000296
Mike Reed99330ba2017-02-22 11:01:08 -0500297 SkBaseDevice* fDevice;
298
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 const DeviceCM* fCurrLayer;
301 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302};
303
Florin Malita713b8ef2017-04-28 10:57:24 -0400304#define FOR_EACH_TOP_DEVICE( code ) \
305 do { \
306 DeviceCM* layer = fMCRec->fTopLayer; \
307 while (layer) { \
308 SkBaseDevice* device = layer->fDevice.get(); \
309 if (device) { \
310 code; \
311 } \
312 layer = layer->fNext; \
313 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500314 } while (0)
315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316/////////////////////////////////////////////////////////////////////////////
317
reeddbc3cef2015-04-29 12:18:57 -0700318/**
319 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700320 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700321 */
reedd053ce92016-03-22 10:17:23 -0700322static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700323 SkImageFilter* imgf = paint.getImageFilter();
324 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700325 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700326 }
327
reedd053ce92016-03-22 10:17:23 -0700328 SkColorFilter* imgCFPtr;
329 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700330 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700331 }
reedd053ce92016-03-22 10:17:23 -0700332 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700333
334 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700335 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700336 // there is no existing paint colorfilter, so we can just return the imagefilter's
337 return imgCF;
338 }
339
340 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
341 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500342 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700343}
344
senorblanco87e066e2015-10-28 11:23:36 -0700345/**
346 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
347 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
348 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
349 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
350 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
351 * conservative "effective" bounds based on the settings in the paint... with one exception. This
352 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
353 * deliberately ignored.
354 */
355static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
356 const SkRect& rawBounds,
357 SkRect* storage) {
358 SkPaint tmpUnfiltered(paint);
359 tmpUnfiltered.setImageFilter(nullptr);
360 if (tmpUnfiltered.canComputeFastBounds()) {
361 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
362 } else {
363 return rawBounds;
364 }
365}
366
Mike Reed38992392019-07-30 10:48:15 -0400367class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368public:
senorblanco87e066e2015-10-28 11:23:36 -0700369 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
370 // paint. It's used to determine the size of the offscreen layer for filters.
371 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400372 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
373 bool skipLayerForImageFilter = false,
374 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400376 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000377 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700378 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
Mike Reed38992392019-07-30 10:48:15 -0400380 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
381 SkASSERT(!fLazyPaint.isValid());
382 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700383 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700384 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700385 fPaint = paint;
386 }
387
388 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700389 /**
390 * We implement ImageFilters for a given draw by creating a layer, then applying the
391 * imagefilter to the pixels of that layer (its backing surface/image), and then
392 * we call restore() to xfer that layer to the main canvas.
393 *
394 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
395 * 2. Generate the src pixels:
396 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
397 * return (fPaint). We then draw the primitive (using srcover) into a cleared
398 * buffer/surface.
399 * 3. Restore the layer created in #1
400 * The imagefilter is passed the buffer/surface from the layer (now filled with the
401 * src pixels of the primitive). It returns a new "filtered" buffer, which we
402 * draw onto the previous layer using the xfermode from the original paint.
403 */
Mike Reed38992392019-07-30 10:48:15 -0400404
405 SkPaint restorePaint;
406 restorePaint.setImageFilter(fPaint->refImageFilter());
407 restorePaint.setBlendMode(fPaint->getBlendMode());
408
senorblanco87e066e2015-10-28 11:23:36 -0700409 SkRect storage;
410 if (rawBounds) {
411 // Make rawBounds include all paint outsets except for those due to image filters.
412 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
413 }
Mike Reed38992392019-07-30 10:48:15 -0400414 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700415 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700416 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000417
Mike Reed38992392019-07-30 10:48:15 -0400418 // Remove the restorePaint fields from our "working" paint
419 SkASSERT(!fLazyPaint.isValid());
420 SkPaint* paint = fLazyPaint.set(origPaint);
421 paint->setImageFilter(nullptr);
422 paint->setBlendMode(SkBlendMode::kSrcOver);
423 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000424 }
425 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000426
Mike Reed38992392019-07-30 10:48:15 -0400427 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700428 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000429 fCanvas->internalRestore();
430 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000431 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000433
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434 const SkPaint& paint() const {
435 SkASSERT(fPaint);
436 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439private:
Mike Reed38992392019-07-30 10:48:15 -0400440 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400442 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000443 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700444 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445};
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447////////// macros to place around the internal draw calls //////////////////
448
Mike Reed38992392019-07-30 10:48:15 -0400449#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700450 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400451 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
452 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800453
454
Mike Reed38992392019-07-30 10:48:15 -0400455#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000456 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400457 AutoLayerForImageFilter draw(this, paint, true); \
458 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000459
Mike Reed38992392019-07-30 10:48:15 -0400460#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000461 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400462 AutoLayerForImageFilter draw(this, paint, false, bounds); \
463 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000464
Mike Reed38992392019-07-30 10:48:15 -0400465#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700466 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400467 AutoLayerForImageFilter draw(this, paint, false, bounds); \
468 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700469
Mike Reed38992392019-07-30 10:48:15 -0400470#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471
472////////////////////////////////////////////////////////////////////////////
473
msarettfbfa2582016-08-12 08:29:08 -0700474static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
475 if (bounds.isEmpty()) {
476 return SkRect::MakeEmpty();
477 }
478
479 // Expand bounds out by 1 in case we are anti-aliasing. We store the
480 // bounds as floats to enable a faster quick reject implementation.
481 SkRect dst;
482 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
483 return dst;
484}
485
mtkleinfeaadee2015-04-08 11:25:48 -0700486void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
487 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700488 fMCRec->reset(bounds);
489
490 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500491 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400492 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700493 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700494 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700495}
496
Hal Canary363a3f82018-10-04 11:04:48 -0400497void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800498 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500501 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500502 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700503 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reeda499f902015-05-01 09:34:31 -0700505 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
506 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400507 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510
halcanary96fcdcc2015-08-27 07:41:13 -0700511 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000512
reedf92c8662014-08-18 08:02:43 -0700513 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700514 // The root device and the canvas should always have the same pixel geometry
515 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800516 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700517 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518
Mike Reedc42a1cd2017-02-14 14:25:14 -0500519 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700520 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400521
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500522 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
reed@google.comcde92112011-07-06 20:00:52 +0000525SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700527 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000528{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000529 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000530
Hal Canary363a3f82018-10-04 11:04:48 -0400531 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000532}
533
reed96a857e2015-01-25 10:33:58 -0800534SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800536 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537{
538 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400539 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500540 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700541}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000542
Hal Canary363a3f82018-10-04 11:04:48 -0400543SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700544 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700545 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700546{
547 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700548
Mike Reed566e53c2017-03-10 10:49:45 -0500549 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400550 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700551}
552
Herb Derbyefe39bc2018-05-01 17:06:20 -0400553SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700555 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000556{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700558
Hal Canary363a3f82018-10-04 11:04:48 -0400559 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700560}
561
reed4a8126e2014-09-22 07:29:03 -0700562SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700563 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700564 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700565{
566 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700567
Mike Reed910ca0f2018-04-25 13:04:05 -0400568 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400569 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700570}
reed29c857d2014-09-21 10:25:07 -0700571
Mike Reed356f7c22017-01-10 11:58:39 -0500572SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
573 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700574 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
575 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500576 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700577{
578 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700579
Mike Reed910ca0f2018-04-25 13:04:05 -0400580 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400581 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582}
583
Mike Reed356f7c22017-01-10 11:58:39 -0500584SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
585
Matt Sarett31f99ce2017-04-11 08:46:01 -0400586#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
587SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
588 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
589 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
590 , fAllocator(nullptr)
591{
592 inc_canvas();
593
594 SkBitmap tmp(bitmap);
595 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400596 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400597 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400598}
599#endif
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601SkCanvas::~SkCanvas() {
602 // free up the contents of our deque
603 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 this->internalRestore(); // restore the last, since we're going away
606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 dec_canvas();
608}
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610///////////////////////////////////////////////////////////////////////////////
611
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000612void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700613 this->onFlush();
614}
615
616void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000617 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000618 if (device) {
619 device->flush();
620 }
621}
622
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500623SkSurface* SkCanvas::getSurface() const {
624 return fSurfaceBase;
625}
626
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000627SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000628 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000629 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
630}
631
senorblancoafc7cce2016-02-02 18:44:15 -0800632SkIRect SkCanvas::getTopLayerBounds() const {
633 SkBaseDevice* d = this->getTopDevice();
634 if (!d) {
635 return SkIRect::MakeEmpty();
636 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500637 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800638}
639
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000640SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000642 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400644 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645}
646
Florin Malita0ed3b642017-01-13 16:56:38 +0000647SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400648 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000649}
650
Mike Reed353196f2017-07-21 11:01:18 -0400651bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000652 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400653 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000654}
655
Mike Reed353196f2017-07-21 11:01:18 -0400656bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
657 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400658}
659
660bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
661 SkPixmap pm;
662 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
663}
664
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000665bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400666 SkPixmap pm;
667 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700668 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000669 }
670 return false;
671}
672
Matt Sarett03dd6d52017-01-23 12:15:09 -0500673bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000675 SkBaseDevice* device = this->getDevice();
676 if (!device) {
677 return false;
678 }
679
Matt Sarett03dd6d52017-01-23 12:15:09 -0500680 // This check gives us an early out and prevents generation ID churn on the surface.
681 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
682 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400683 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500684 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000686
Matt Sarett03dd6d52017-01-23 12:15:09 -0500687 // Tell our owning surface to bump its generation ID.
688 const bool completeOverwrite =
689 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700690 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700691
Matt Sarett03dd6d52017-01-23 12:15:09 -0500692 // This can still fail, most notably in the case of a invalid color type or alpha type
693 // conversion. We could pull those checks into this function and avoid the unnecessary
694 // generation ID bump. But then we would be performing those checks twice, since they
695 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400696 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000697}
reed@google.com51df9e32010-12-23 19:29:18 +0000698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699//////////////////////////////////////////////////////////////////////////////
700
reed2ff1fce2014-12-11 07:07:37 -0800701void SkCanvas::checkForDeferredSave() {
702 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800703 this->doSave();
704 }
705}
706
reedf0090cb2014-11-26 08:55:51 -0800707int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800708#ifdef SK_DEBUG
709 int count = 0;
710 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
711 for (;;) {
712 const MCRec* rec = (const MCRec*)iter.next();
713 if (!rec) {
714 break;
715 }
716 count += 1 + rec->fDeferredSaveCount;
717 }
718 SkASSERT(count == fSaveCount);
719#endif
720 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800721}
722
723int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800724 fSaveCount += 1;
725 fMCRec->fDeferredSaveCount += 1;
726 return this->getSaveCount() - 1; // return our prev value
727}
728
729void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800730 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700731
732 SkASSERT(fMCRec->fDeferredSaveCount > 0);
733 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800734 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800735}
736
Mike Reedab7e8b32020-04-03 15:33:40 -0400737void SkCanvas::notifyCameraChanged() {
738 SkM44 invc; // start with identity
739 if (!fCameraStack.empty()) {
740 invc = fCameraStack.back().fInvPostCamera;
741 }
742 FOR_EACH_TOP_DEVICE(device->setInvCamera(invc));
743}
744
Mike Reed3ef77dd2020-04-06 10:41:09 -0400745int SkCanvas::saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500746 // TODO: add a virtual for this, and update clients (e.g. chrome)
747 int n = this->save();
Mike Reed3ef77dd2020-04-06 10:41:09 -0400748 this->concat(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500749 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedab7e8b32020-04-03 15:33:40 -0400750 this->notifyCameraChanged();
751
Mike Reedee0a03a2020-01-14 16:44:47 -0500752 return n;
753}
754
reedf0090cb2014-11-26 08:55:51 -0800755void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800756 if (fMCRec->fDeferredSaveCount > 0) {
757 SkASSERT(fSaveCount > 1);
758 fSaveCount -= 1;
759 fMCRec->fDeferredSaveCount -= 1;
760 } else {
761 // check for underflow
762 if (fMCStack.count() > 1) {
763 this->willRestore();
764 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700765 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800766 this->internalRestore();
767 this->didRestore();
768 }
reedf0090cb2014-11-26 08:55:51 -0800769 }
770}
771
772void SkCanvas::restoreToCount(int count) {
773 // sanity check
774 if (count < 1) {
775 count = 1;
776 }
mtkleinf0f14112014-12-12 08:46:25 -0800777
reedf0090cb2014-11-26 08:55:51 -0800778 int n = this->getSaveCount() - count;
779 for (int i = 0; i < n; ++i) {
780 this->restore();
781 }
782}
783
reed2ff1fce2014-12-11 07:07:37 -0800784void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700786 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000788
Mike Reedc42a1cd2017-02-14 14:25:14 -0500789 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790}
791
reed4960eee2015-12-18 07:09:18 -0800792bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400793 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794}
795
reed4960eee2015-12-18 07:09:18 -0800796bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700797 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400798 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
799 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
800 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
801 // filter does not require a decomposed CTM matrix, the filter space and device space are the
802 // same. When it has been decomposed, we want the original image filter node to process the
803 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
804 // longer the original filter, but has the remainder matrix baked into it, and passing in the
805 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
806 // to the original filter node (barring inflation from consecutive calls to mapRect). While
807 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
808 // passing getDeviceClipBounds() to 'imageFilter' is correct.
809 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
810 // be important to more accurately calculate the clip bounds in the layer space for the original
811 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500812 SkIRect clipBounds = this->getDeviceClipBounds();
813 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000814 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000815 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000816
reed96e657d2015-03-10 17:30:07 -0700817 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
818
Robert Phillips12078432018-05-17 11:17:39 -0400819 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
820 // If the image filter DAG affects transparent black then we will need to render
821 // out to the clip bounds
822 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000823 }
Robert Phillips12078432018-05-17 11:17:39 -0400824
825 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700826 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700828 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400829 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400831 inputSaveLayerBounds = clipBounds;
832 }
833
834 if (imageFilter) {
835 // expand the clip bounds by the image filter DAG to include extra content that might
836 // be required by the image filters.
837 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
838 SkImageFilter::kReverse_MapDirection,
839 &inputSaveLayerBounds);
840 }
841
842 SkIRect clippedSaveLayerBounds;
843 if (bounds) {
844 // For better or for worse, user bounds currently act as a hard clip on the layer's
845 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
846 clippedSaveLayerBounds = inputSaveLayerBounds;
847 } else {
848 // If there are no user bounds, we don't want to artificially restrict the resulting
849 // layer bounds, so allow the expanded clip bounds free reign.
850 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800852
853 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400854 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800855 if (BoundsAffectsClip(saveLayerFlags)) {
856 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
857 fMCRec->fRasterClip.setEmpty();
858 fDeviceClipBounds.setEmpty();
859 }
860 return false;
861 }
Robert Phillips12078432018-05-17 11:17:39 -0400862 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863
reed4960eee2015-12-18 07:09:18 -0800864 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700865 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400866 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
867 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000868 }
869
870 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400871 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000872 }
Robert Phillips12078432018-05-17 11:17:39 -0400873
junov@chromium.orga907ac32012-02-24 21:54:07 +0000874 return true;
875}
876
reed4960eee2015-12-18 07:09:18 -0800877int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
878 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000879}
880
Cary Clarke041e312018-03-06 13:00:52 -0500881int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700882 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400883 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
884 // no need for the layer (or any of the draws until the matching restore()
885 this->save();
886 this->clipRect({0,0,0,0});
887 } else {
888 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
889 fSaveCount += 1;
890 this->internalSaveLayer(rec, strategy);
891 }
reed4960eee2015-12-18 07:09:18 -0800892 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800893}
894
Mike Reed148b7fd2018-12-18 17:38:18 -0500895int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
896 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
897 // Assuming clips never expand, if the request bounds is outside of the current clip
898 // there is no need to copy/restore the area, so just devolve back to a regular save.
899 this->save();
900 } else {
901 bool doTheWork = this->onDoSaveBehind(bounds);
902 fSaveCount += 1;
903 this->internalSave();
904 if (doTheWork) {
905 this->internalSaveBehind(bounds);
906 }
907 }
908 return this->getSaveCount() - 1;
909}
910
reeda2217ef2016-07-20 06:04:34 -0700911void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500912 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500913 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400914 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
915 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400916 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500917 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
918 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400919 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400920 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
921 // This means that we only have to copy a dst-sized block of pixels out of src and translate
922 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400923 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
924 dstOrigin.y() - src->getOrigin().y(),
925 dst->width(), dst->height());
926 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400927 return;
928 }
929
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400930 auto special = src->snapSpecial(snapBounds);
931 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400932 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
933 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400934 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
935 }
936 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400937 }
938
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400939 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
940 // by the backdrop filter.
941 SkMatrix toRoot, layerMatrix;
942 SkSize scale;
943 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
944 toRoot = SkMatrix::I();
945 layerMatrix = ctm;
946 } else if (ctm.decomposeScale(&scale, &toRoot)) {
947 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
948 } else {
949 // Perspective, for now, do no scaling of the layer itself.
950 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
951 // the matrix, e.g. based on the midpoint of the near/far planes?
952 toRoot = ctm;
953 layerMatrix = SkMatrix::I();
954 }
955
956 // We have to map the dst bounds from the root space into the layer space where filtering will
957 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
958 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
959 // is a safe, conservative estimate.
960 SkMatrix fromRoot;
961 if (!toRoot.invert(&fromRoot)) {
962 return;
963 }
964
965 // This represents what the backdrop filter needs to produce in the layer space, and is sized
966 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
967 SkIRect layerTargetBounds = fromRoot.mapRect(
968 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
969 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
970 // require some extra input pixels.
971 SkIRect layerInputBounds = filter->filterBounds(
972 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
973 &layerTargetBounds);
974
975 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400976 // be the conservative contents required to fill a layerInputBounds-sized surface with the
977 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400978 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
979 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
980 if (!backdropBounds.intersect(srcDevRect)) {
981 return;
982 }
983
984 auto special = src->snapSpecial(backdropBounds);
985 if (!special) {
986 return;
987 }
988
989 SkColorType colorType = src->imageInfo().colorType();
990 if (colorType == kUnknown_SkColorType) {
991 colorType = kRGBA_8888_SkColorType;
992 }
993 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400994
995 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400996 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400997 // Drawing the temporary and final filtered image requires a higher filter quality if the
998 // 'toRoot' transformation is not identity, in order to minimize the impact on already
999 // rendered edges/content.
1000 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
1001 p.setFilterQuality(kHigh_SkFilterQuality);
1002
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001003 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1004 // and stored in a temporary surface, which is then used as the input to the actual filter.
1005 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1006 if (!tmpSurface) {
1007 return;
1008 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001009
1010 auto tmpCanvas = tmpSurface->getCanvas();
1011 tmpCanvas->clear(SK_ColorTRANSPARENT);
1012 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1013 // space, then maps from root space into the layer space, then maps it so the input layer's
1014 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1015 // performed on backdropBounds.
1016 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1017 tmpCanvas->concat(fromRoot);
1018 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001019
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001020 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1021 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1022 special = tmpSurface->makeImageSnapshot();
1023 } else {
1024 // Since there is no extra transform that was done, update the input bounds to reflect
1025 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1026 // was equal to backdropBounds before it was made relative to the src device and cropped.
1027 // When we use the original snapped image directly, just map the update backdrop bounds
1028 // back into the shared layer space
1029 layerInputBounds = backdropBounds;
1030 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001031
1032 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1033 // draw will be 1-1 so there is no need to increase filter quality.
1034 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001035 }
1036
1037 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1038 // layer space. This has to further offset everything so that filter evaluation thinks the
1039 // source image's top left corner is (0, 0).
1040 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1041 // this can be simplified.
1042 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1043 SkMatrix filterCTM = layerMatrix;
1044 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1045 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1046
1047 SkIPoint offset;
1048 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001049 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001050 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1051 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1052 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1053 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001054 offset += layerInputBounds.topLeft();
1055
1056 // Manually setting the device's CTM requires accounting for the device's origin.
1057 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001058 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001059 // a global CTM instead of a device CTM.
1060 SkMatrix dstCTM = toRoot;
1061 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001062 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001063
1064 // And because devices don't have a special-image draw function that supports arbitrary
1065 // matrices, we are abusing the asImage() functionality here...
1066 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001067 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001068 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001069 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001070 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1071 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001072 }
robertphillips7354a4b2015-12-16 05:08:27 -08001073}
reed70ee31b2015-12-10 13:44:45 -08001074
Mike Kleine083f7c2018-02-07 12:54:27 -05001075static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001076 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001077 if (prev.bytesPerPixel() <= 4 &&
1078 prev.colorType() != kRGBA_8888_SkColorType &&
1079 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001080 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1081 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1082 ct = kN32_SkColorType;
1083 }
1084 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001085}
1086
reed4960eee2015-12-18 07:09:18 -08001087void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001088 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001089 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001090 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1091
Michael Ludwigeced98b2020-03-03 10:39:41 -05001092 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1093 // saveLayer ignores mask filters, so force it to null
1094 if (paint.get() && paint->getMaskFilter()) {
1095 paint.writable()->setMaskFilter(nullptr);
1096 }
1097
Mike Reed5532c2a2019-02-23 12:00:32 -05001098 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1099 // regardless of any hint-rect from the caller. skbug.com/8783
1100 if (rec.fBackdrop) {
1101 bounds = nullptr;
1102 }
1103
Michael Ludwigeced98b2020-03-03 10:39:41 -05001104 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001105 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001106 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001107
reed8c30a812016-04-20 16:36:51 -07001108 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001109 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1110 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1111 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001112 *
1113 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001114 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1115 * if necessary.
1116 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1117 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001118 * 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 -04001119 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001120 * of the original imagefilter, and draw that (via drawSprite)
1121 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1122 *
1123 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1124 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1125 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001126 if (imageFilter) {
1127 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001128 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1129 &modifiedCTM);
1130 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001131 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001132 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001133 modifiedRec = fMCRec;
1134 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001135 imageFilter = modifiedFilter.get();
1136 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001137 }
1138 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1139 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001140 }
reed8c30a812016-04-20 16:36:51 -07001141
junov@chromium.orga907ac32012-02-24 21:54:07 +00001142 // do this before we create the layer. We don't call the public save() since
1143 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001144 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001145
junov@chromium.orga907ac32012-02-24 21:54:07 +00001146 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001147 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001148 if (modifiedRec) {
1149 // In this case there will be no layer in which to stash the matrix so we need to
1150 // revert the prior MCRec to its earlier state.
1151 modifiedRec->fMatrix = stashedMatrix;
1152 }
reed2ff1fce2014-12-11 07:07:37 -08001153 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 }
1155
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001156 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1157 // the clipRectBounds() call above?
1158 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001159 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001160 }
1161
reed8dc0ccb2015-03-20 06:32:52 -07001162 SkPixelGeometry geo = fProps.pixelGeometry();
1163 if (paint) {
reed76033be2015-03-14 10:54:31 -07001164 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001165 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001166 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001167 }
1168 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169
robertphillips5139e502016-07-19 05:10:40 -07001170 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001171 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001172 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001173 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001174 }
reedb2db8982014-11-13 12:41:02 -08001175
Mike Kleine083f7c2018-02-07 12:54:27 -05001176 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001177 if (rec.fSaveLayerFlags & kF16ColorType) {
1178 info = info.makeColorType(kRGBA_F16_SkColorType);
1179 }
reed129ed1c2016-02-22 06:42:31 -08001180
Hal Canary704cd322016-11-07 14:13:52 -05001181 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001182 {
Florin Malita4571e492019-07-16 10:25:58 -04001183 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001184 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001185 const bool trackCoverage =
1186 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001187 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001188 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001189 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001190 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1191 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001192 return;
reed61f501f2015-04-29 08:34:00 -07001193 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001194 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001195 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196
Mike Reedb43a3e02017-02-11 10:18:58 -05001197 // only have a "next" if this new layer doesn't affect the clip (rare)
1198 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 fMCRec->fLayer = layer;
1200 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001201
Mike Reedc61abee2017-02-28 17:45:27 -05001202 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001203 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001204 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001205 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001206
Mike Reedc42a1cd2017-02-14 14:25:14 -05001207 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1208
1209 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1210 if (layer->fNext) {
1211 // need to punch a hole in the previous device, so we don't draw there, given that
1212 // the new top-layer will allow drawing to happen "below" it.
1213 SkRegion hole(ir);
1214 do {
1215 layer = layer->fNext;
1216 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1217 } while (layer->fNext);
1218 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219}
1220
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001221int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001222 if (0xFF == alpha) {
1223 return this->saveLayer(bounds, nullptr);
1224 } else {
1225 SkPaint tmpPaint;
1226 tmpPaint.setAlpha(alpha);
1227 return this->saveLayer(bounds, &tmpPaint);
1228 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001229}
1230
Mike Reed148b7fd2018-12-18 17:38:18 -05001231void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001232 SkBaseDevice* device = this->getTopDevice();
1233 if (nullptr == device) { // Do we still need this check???
1234 return;
1235 }
1236
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001237 // Map the local bounds into the top device's coordinate space (this is not
1238 // necessarily the full global CTM transform).
1239 SkIRect devBounds;
1240 if (localBounds) {
1241 SkRect tmp;
1242 device->localToDevice().mapRect(&tmp, *localBounds);
1243 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1244 devBounds.setEmpty();
1245 }
1246 } else {
1247 devBounds = device->devClipBounds();
1248 }
1249 if (devBounds.isEmpty()) {
1250 return;
1251 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001252
Michael Ludwigac352122019-08-28 21:03:05 +00001253 // This is getting the special image from the current device, which is then drawn into (both by
1254 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1255 // own device, we need to explicitly copy the back image contents so that its original content
1256 // is available when we splat it back later during restore.
1257 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001258 if (!backImage) {
1259 return;
1260 }
1261
1262 // we really need the save, so we can wack the fMCRec
1263 this->checkForDeferredSave();
1264
1265 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1266
1267 SkPaint paint;
1268 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001269 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001270}
1271
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272void SkCanvas::internalRestore() {
1273 SkASSERT(fMCStack.count() != 0);
1274
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001275 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 DeviceCM* layer = fMCRec->fLayer; // may be null
1277 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001278 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
Mike Reed148b7fd2018-12-18 17:38:18 -05001280 // move this out before we do the actual restore
1281 auto backImage = std::move(fMCRec->fBackImage);
1282
Mike Reedb18e74d2020-01-16 13:58:22 -05001283 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1284 fCameraStack.pop_back();
Mike Reedab7e8b32020-04-03 15:33:40 -04001285 this->notifyCameraChanged();
Mike Reedb18e74d2020-01-16 13:58:22 -05001286 }
1287
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 // now do the normal restore()
1289 fMCRec->~MCRec(); // balanced in save()
1290 fMCStack.pop_back();
1291 fMCRec = (MCRec*)fMCStack.back();
1292
Mike Reedc42a1cd2017-02-14 14:25:14 -05001293 if (fMCRec) {
1294 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1295 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001296
Mike Reed148b7fd2018-12-18 17:38:18 -05001297 if (backImage) {
1298 SkPaint paint;
1299 paint.setBlendMode(SkBlendMode::kDstOver);
1300 const int x = backImage->fLoc.x();
1301 const int y = backImage->fLoc.y();
1302 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1303 nullptr, SkMatrix::I());
1304 }
1305
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1307 since if we're being recorded, we don't want to record this (the
1308 recorder will have already recorded the restore).
1309 */
bsalomon49f085d2014-09-05 13:34:00 -07001310 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001311 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001312 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001313 // At this point, 'layer' has been removed from the device stack, so the devices that
1314 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1315 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001316 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001317 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001318 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001319 delete layer;
reedb679ca82015-04-07 04:40:48 -07001320 } else {
1321 // we're at the root
reeda499f902015-05-01 09:34:31 -07001322 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001323 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001324 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001326 }
msarettfbfa2582016-08-12 08:29:08 -07001327
1328 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001329 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001330 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1331 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332}
1333
reede8f30622016-03-23 18:59:25 -07001334sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001335 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001336 props = &fProps;
1337 }
1338 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001339}
1340
reede8f30622016-03-23 18:59:25 -07001341sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001342 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001343 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001344}
1345
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001346SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001347 return this->onImageInfo();
1348}
1349
1350SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001351 SkBaseDevice* dev = this->getDevice();
1352 if (dev) {
1353 return dev->imageInfo();
1354 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001355 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001356 }
1357}
1358
brianosman898235c2016-04-06 07:38:23 -07001359bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001360 return this->onGetProps(props);
1361}
1362
1363bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001364 SkBaseDevice* dev = this->getDevice();
1365 if (dev) {
1366 if (props) {
1367 *props = fProps;
1368 }
1369 return true;
1370 } else {
1371 return false;
1372 }
1373}
1374
reed6ceeebd2016-03-09 14:26:26 -08001375bool SkCanvas::peekPixels(SkPixmap* pmap) {
1376 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377}
1378
reed884e97c2015-05-26 11:31:54 -07001379bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001380 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001381 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001382}
1383
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001384void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001385 SkPixmap pmap;
1386 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001387 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001388 }
1389 if (info) {
1390 *info = pmap.info();
1391 }
1392 if (rowBytes) {
1393 *rowBytes = pmap.rowBytes();
1394 }
1395 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001396 // If the caller requested the origin, they presumably are expecting the returned pixels to
1397 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1398 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1399 // instead of an origin, just don't expose the pixels in that case. Note that this means
1400 // that layers with complex coordinate spaces can still report their pixels if the caller
1401 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1402 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1403 *origin = this->getTopDevice()->getOrigin();
1404 } else {
1405 return nullptr;
1406 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001407 }
reed884e97c2015-05-26 11:31:54 -07001408 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001409}
1410
reed884e97c2015-05-26 11:31:54 -07001411bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001412 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001413 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001414}
1415
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417
Mike Reed8bcd1282019-03-13 16:51:54 -04001418// In our current design/features, we should never have a layer (src) in a different colorspace
1419// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1420// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1421// colorspace.
1422static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1423 SkASSERT(src == dst);
1424}
1425
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001426void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001427 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001429 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 paint = &tmp;
1431 }
reed@google.com4b226022011-01-11 18:32:13 +00001432
Mike Reed38992392019-07-30 10:48:15 -04001433 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001434
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001436 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001437 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1438 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001439 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001440 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001441 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1442 // so it should always be possible to use the relative origin. Once drawDevice() and
1443 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1444 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001445 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001446 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1447 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001448 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1449 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001450 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1451 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001452 }
reed@google.com76dd2772012-01-05 21:15:07 +00001453 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001454 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001455 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 }
reeda2217ef2016-07-20 06:04:34 -07001457
Mike Reed38992392019-07-30 10:48:15 -04001458 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459}
1460
reed32704672015-12-16 08:27:10 -08001461/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001464 if (dx || dy) {
1465 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001466 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001467
reedfe69b502016-09-12 06:31:48 -07001468 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001469 // However, if either is non-finite, we might still complicate the matrix type,
1470 // so we still have to compute this.
1471 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001472
Mike Reedc42a1cd2017-02-14 14:25:14 -05001473 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001474
reedfe69b502016-09-12 06:31:48 -07001475 this->didTranslate(dx,dy);
1476 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001479void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001480 if (sx != 1 || sy != 1) {
1481 this->checkForDeferredSave();
1482 fMCRec->fMatrix.preScale(sx, sy);
1483
1484 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1485 // but pre-scaling by a non-finite does change it, so we have to recompute.
1486 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1487
1488 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1489
1490 this->didScale(sx, sy);
1491 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492}
1493
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001494void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001495 SkMatrix m;
1496 m.setRotate(degrees);
1497 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498}
1499
bungeman7438bfc2016-07-12 15:01:19 -07001500void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1501 SkMatrix m;
1502 m.setRotate(degrees, px, py);
1503 this->concat(m);
1504}
1505
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001506void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001507 SkMatrix m;
1508 m.setSkew(sx, sy);
1509 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001510}
1511
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001512void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001513 if (matrix.isIdentity()) {
1514 return;
1515 }
1516
reed2ff1fce2014-12-11 07:07:37 -08001517 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001518 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001519
msarett9637ea92016-08-18 14:03:30 -07001520 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001521
Mike Reed7627fa52017-02-08 10:07:53 -05001522 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001523
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001524 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001525}
1526
Mike Reed3ef77dd2020-04-06 10:41:09 -04001527void SkCanvas::concat(const SkM44& m) {
Mike Reed403c8072020-01-08 10:40:39 -05001528 this->checkForDeferredSave();
1529
Mike Reed3ef77dd2020-04-06 10:41:09 -04001530 fMCRec->fMatrix.preConcat(m);
Mike Reed403c8072020-01-08 10:40:39 -05001531
1532 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1533
1534 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1535
Mike Reed3ef77dd2020-04-06 10:41:09 -04001536 this->didConcat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001537}
1538
reed8c30a812016-04-20 16:36:51 -07001539void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001540 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001541 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001542
Mike Reedc42a1cd2017-02-14 14:25:14 -05001543 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001544}
1545
1546void SkCanvas::setMatrix(const SkMatrix& matrix) {
1547 this->checkForDeferredSave();
1548 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001549 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550}
1551
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001553 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554}
1555
1556//////////////////////////////////////////////////////////////////////////////
1557
Mike Reedc1f77742016-12-09 09:00:50 -05001558void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001559 if (!rect.isFinite()) {
1560 return;
1561 }
reed2ff1fce2014-12-11 07:07:37 -08001562 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1564 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565}
1566
Mike Reedc1f77742016-12-09 09:00:50 -05001567void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001568 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001569
Mike Reed7627fa52017-02-08 10:07:53 -05001570 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001571
reedc64eff52015-11-21 12:39:45 -08001572 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001573 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1574 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001575 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576}
1577
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001578void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1579 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001580 if (fClipRestrictionRect.isEmpty()) {
1581 // we notify the device, but we *dont* resolve deferred saves (since we're just
1582 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001583 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001584 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001585 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001586 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001587 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001588 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001589 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1590 }
1591}
1592
Mike Reedc1f77742016-12-09 09:00:50 -05001593void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001594 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001596 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001597 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1598 } else {
1599 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001600 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001601}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001602
Mike Reedc1f77742016-12-09 09:00:50 -05001603void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001604 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001605
Brian Salomona3b45d42016-10-03 11:36:16 -04001606 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001607
Mike Reed7627fa52017-02-08 10:07:53 -05001608 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001609
Mike Reed20800c82017-11-15 16:09:04 -05001610 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1611 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001612 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001613}
1614
Mike Reedc1f77742016-12-09 09:00:50 -05001615void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001616 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001617 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001618
1619 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1620 SkRect r;
1621 if (path.isRect(&r)) {
1622 this->onClipRect(r, op, edgeStyle);
1623 return;
1624 }
1625 SkRRect rrect;
1626 if (path.isOval(&r)) {
1627 rrect.setOval(r);
1628 this->onClipRRect(rrect, op, edgeStyle);
1629 return;
1630 }
1631 if (path.isRRect(&rrect)) {
1632 this->onClipRRect(rrect, op, edgeStyle);
1633 return;
1634 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635 }
robertphillips39f05382015-11-24 09:30:12 -08001636
1637 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001638}
1639
Mike Reedc1f77742016-12-09 09:00:50 -05001640void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001641 AutoValidateClip avc(this);
1642
Brian Salomona3b45d42016-10-03 11:36:16 -04001643 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001644
Mike Reed7627fa52017-02-08 10:07:53 -05001645 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646
Brian Salomona3b45d42016-10-03 11:36:16 -04001647 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001648 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001649 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001650 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651}
1652
Mike Reed121c2af2020-03-10 14:02:56 -04001653void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1654 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001655 if (sh->isOpaque()) {
1656 if (op == SkClipOp::kIntersect) {
1657 // we don't occlude anything, so skip this call
1658 } else {
1659 SkASSERT(op == SkClipOp::kDifference);
1660 // we occlude everything, so set the clip to empty
1661 this->clipRect({0,0,0,0});
1662 }
1663 } else {
1664 this->onClipShader(std::move(sh), op);
1665 }
Mike Reed121c2af2020-03-10 14:02:56 -04001666 }
1667}
1668
1669void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1670 AutoValidateClip avc(this);
1671
1672 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1673
1674 // we don't know how to mutate our conservative bounds, so we don't
1675}
1676
Mike Reedc1f77742016-12-09 09:00:50 -05001677void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001678 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001680}
1681
Mike Reedc1f77742016-12-09 09:00:50 -05001682void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001683 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001684
reed@google.com5c3d1472011-02-22 19:12:23 +00001685 AutoValidateClip avc(this);
1686
Mike Reed20800c82017-11-15 16:09:04 -05001687 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001688 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689}
1690
reed@google.com819c9212011-02-23 18:56:55 +00001691#ifdef SK_DEBUG
1692void SkCanvas::validateClip() const {
1693 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001694 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001695 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001696 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001697 return;
1698 }
reed@google.com819c9212011-02-23 18:56:55 +00001699}
1700#endif
1701
Mike Reeda1361362017-03-07 09:37:29 -05001702bool SkCanvas::androidFramework_isClipAA() const {
1703 bool containsAA = false;
1704
1705 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1706
1707 return containsAA;
1708}
1709
1710class RgnAccumulator {
1711 SkRegion* fRgn;
1712public:
1713 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1714 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1715 SkIPoint origin = device->getOrigin();
1716 if (origin.x() | origin.y()) {
1717 rgn->translate(origin.x(), origin.y());
1718 }
1719 fRgn->op(*rgn, SkRegion::kUnion_Op);
1720 }
1721};
1722
1723void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1724 RgnAccumulator accum(rgn);
1725 SkRegion tmp;
1726
1727 rgn->setEmpty();
1728 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001729}
1730
reed@google.com5c3d1472011-02-22 19:12:23 +00001731///////////////////////////////////////////////////////////////////////////////
1732
reed@google.com754de5f2014-02-24 19:38:20 +00001733bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001734 return fMCRec->fRasterClip.isEmpty();
1735
1736 // TODO: should we only use the conservative answer in a recording canvas?
1737#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001738 SkBaseDevice* dev = this->getTopDevice();
1739 // if no device we return true
1740 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001741#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001742}
1743
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001744bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001745 SkBaseDevice* dev = this->getTopDevice();
1746 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001747 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001748}
1749
msarettfbfa2582016-08-12 08:29:08 -07001750static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1751#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1752 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1753 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1754 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1755 return 0xF != _mm_movemask_ps(mask);
1756#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1757 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1758 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1759 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1760 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1761#else
1762 SkRect devRectAsRect;
1763 SkRect devClipAsRect;
1764 devRect.store(&devRectAsRect.fLeft);
1765 devClip.store(&devClipAsRect.fLeft);
1766 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1767#endif
1768}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001769
msarettfbfa2582016-08-12 08:29:08 -07001770// It's important for this function to not be inlined. Otherwise the compiler will share code
1771// between the fast path and the slow path, resulting in two slow paths.
1772static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1773 const SkMatrix& matrix) {
1774 SkRect deviceRect;
1775 matrix.mapRect(&deviceRect, src);
1776 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1777}
1778
1779bool SkCanvas::quickReject(const SkRect& src) const {
1780#ifdef SK_DEBUG
1781 // Verify that fDeviceClipBounds are set properly.
1782 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001783 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001784 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001785 } else {
msarettfbfa2582016-08-12 08:29:08 -07001786 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 }
msarettfbfa2582016-08-12 08:29:08 -07001788
msarett9637ea92016-08-18 14:03:30 -07001789 // Verify that fIsScaleTranslate is set properly.
1790 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001791#endif
1792
msarett9637ea92016-08-18 14:03:30 -07001793 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001794 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1795 }
1796
1797 // We inline the implementation of mapScaleTranslate() for the fast path.
1798 float sx = fMCRec->fMatrix.getScaleX();
1799 float sy = fMCRec->fMatrix.getScaleY();
1800 float tx = fMCRec->fMatrix.getTranslateX();
1801 float ty = fMCRec->fMatrix.getTranslateY();
1802 Sk4f scale(sx, sy, sx, sy);
1803 Sk4f trans(tx, ty, tx, ty);
1804
1805 // Apply matrix.
1806 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1807
1808 // Make sure left < right, top < bottom.
1809 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1810 Sk4f min = Sk4f::Min(ltrb, rblt);
1811 Sk4f max = Sk4f::Max(ltrb, rblt);
1812 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1813 // ARM this sequence generates the fastest (a single instruction).
1814 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1815
1816 // Check if the device rect is NaN or outside the clip.
1817 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818}
1819
reed@google.com3b3e8952012-08-16 20:53:31 +00001820bool SkCanvas::quickReject(const SkPath& path) const {
1821 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822}
1823
Mike Klein83c8dd92017-11-28 17:08:45 -05001824SkRect SkCanvas::getLocalClipBounds() const {
1825 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001826 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001827 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828 }
1829
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001830 SkMatrix inverse;
1831 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001832 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001833 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001834 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835
Mike Reed42e8c532017-01-23 14:09:13 -05001836 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001837 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001838 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001839
Mike Reedb57b9312018-04-23 12:12:54 -04001840 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001841 inverse.mapRect(&bounds, r);
1842 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843}
1844
Mike Klein83c8dd92017-11-28 17:08:45 -05001845SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001846 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001847}
1848
Mike Reedb18e74d2020-01-16 13:58:22 -05001849///////////////////////////////////////////////////////////////////////
1850
1851SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1852 : fMCRec(owner)
1853 , fCamera(camera)
1854{
1855 // assumes the mcrec has already been concatenated with the camera
1856 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1857 fInvPostCamera.setIdentity();
1858 }
1859}
1860
Mike Reed403c8072020-01-08 10:40:39 -05001861SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001862 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863}
1864
Mike Reed46f5c5f2020-02-20 15:42:29 -05001865SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001866 return fMCRec->fMatrix;
1867}
1868
Mike Reedc43f2a02020-01-16 14:54:34 -05001869SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001870 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001871 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001872 } else {
1873 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001874 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001875 }
1876}
1877
Mike Reedc43f2a02020-01-16 14:54:34 -05001878SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001879 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001880 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001881 } else {
1882 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001883 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001884 }
1885}
1886
Brian Osman11052242016-10-27 14:47:55 -04001887GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001888 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001889 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001890}
1891
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001892GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001893 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001894 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001895}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001896
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001897void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1898 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001899 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001900 if (outer.isEmpty()) {
1901 return;
1902 }
1903 if (inner.isEmpty()) {
1904 this->drawRRect(outer, paint);
1905 return;
1906 }
1907
1908 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001909 // be able to return ...
1910 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001911 //
1912 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001913 if (!outer.getBounds().contains(inner.getBounds())) {
1914 return;
1915 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001916
1917 this->onDrawDRRect(outer, inner, paint);
1918}
1919
reed41af9662015-01-05 07:49:08 -08001920void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001921 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001922 this->onDrawPaint(paint);
1923}
1924
1925void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001926 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001927 // To avoid redundant logic in our culling code and various backends, we always sort rects
1928 // before passing them along.
1929 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001930}
1931
Mike Reedd5674082019-04-19 15:00:47 -04001932void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1933 TRACE_EVENT0("skia", TRACE_FUNC);
1934 this->onDrawBehind(paint);
1935}
1936
msarettdca352e2016-08-26 06:37:45 -07001937void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001938 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001939 if (region.isEmpty()) {
1940 return;
1941 }
1942
1943 if (region.isRect()) {
1944 return this->drawIRect(region.getBounds(), paint);
1945 }
1946
1947 this->onDrawRegion(region, paint);
1948}
1949
reed41af9662015-01-05 07:49:08 -08001950void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001951 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001952 // To avoid redundant logic in our culling code and various backends, we always sort rects
1953 // before passing them along.
1954 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001955}
1956
1957void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001958 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001959 this->onDrawRRect(rrect, paint);
1960}
1961
1962void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001963 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001964 this->onDrawPoints(mode, count, pts, paint);
1965}
1966
Mike Reede88a1cb2017-03-17 09:50:46 -04001967void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1968 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001969 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001970}
1971
1972void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001973 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001974 RETURN_ON_NULL(vertices);
Brian Osmanf11e3312020-03-24 14:57:38 -04001975
Mike Reed5caf9352020-03-02 14:57:09 -05001976 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Brian Osman8cbedf92020-03-31 10:38:31 -04001977 SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);
Brian Osmanf11e3312020-03-24 14:57:38 -04001978
1979 // If the vertices contain custom attributes, ensure they line up with the paint's shader
1980 const SkRuntimeEffect* effect =
1981 paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001982 if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) {
Brian Osmanf11e3312020-03-24 14:57:38 -04001983 return;
1984 }
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001985 if (effect) {
1986 int attrIndex = 0;
1987 for (const auto& v : effect->varyings()) {
1988 if (vertices->priv().attributes()[attrIndex++].channelCount() != v.fWidth) {
1989 return;
1990 }
1991 }
1992 }
Brian Osmanf11e3312020-03-24 14:57:38 -04001993
Mike Reed5caf9352020-03-02 14:57:09 -05001994 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001995}
1996
1997void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001998 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001999 this->onDrawPath(path, paint);
2000}
2001
reeda85d4d02015-05-06 12:56:48 -07002002void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002003 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002004 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07002005 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08002006}
2007
Mike Reedc4e31092018-01-30 11:15:27 -05002008// Returns true if the rect can be "filled" : non-empty and finite
2009static bool fillable(const SkRect& r) {
2010 SkScalar w = r.width();
2011 SkScalar h = r.height();
2012 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
2013}
2014
reede47829b2015-08-06 10:02:53 -07002015void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2016 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002017 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002018 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002019 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002020 return;
2021 }
2022 this->onDrawImageRect(image, &src, dst, paint, constraint);
2023}
reed41af9662015-01-05 07:49:08 -08002024
reed84984ef2015-07-17 07:09:43 -07002025void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2026 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002027 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002028 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002029}
2030
Brian Salomonf08002c2018-10-26 16:15:46 -04002031void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002032 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002033 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002034 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002035}
reede47829b2015-08-06 10:02:53 -07002036
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002037namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002038class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002039public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002040 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2041 if (!origPaint) {
2042 return;
2043 }
2044 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2045 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2046 }
2047 if (origPaint->getMaskFilter()) {
2048 fPaint.writable()->setMaskFilter(nullptr);
2049 }
2050 if (origPaint->isAntiAlias()) {
2051 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002052 }
2053 }
2054
2055 const SkPaint* get() const {
2056 return fPaint;
2057 }
2058
2059private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002060 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002061};
2062} // namespace
2063
reed4c21dc52015-06-25 12:32:03 -07002064void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2065 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002066 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002067 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002068 if (dst.isEmpty()) {
2069 return;
2070 }
msarett552bca92016-08-03 06:53:26 -07002071 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002072 LatticePaint latticePaint(paint);
2073 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002074 } else {
reede47829b2015-08-06 10:02:53 -07002075 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002076 }
reed4c21dc52015-06-25 12:32:03 -07002077}
2078
msarett16882062016-08-16 09:31:08 -07002079void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2080 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002081 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002082 RETURN_ON_NULL(image);
2083 if (dst.isEmpty()) {
2084 return;
2085 }
msarett71df2d72016-09-30 12:41:42 -07002086
2087 SkIRect bounds;
2088 Lattice latticePlusBounds = lattice;
2089 if (!latticePlusBounds.fBounds) {
2090 bounds = SkIRect::MakeWH(image->width(), image->height());
2091 latticePlusBounds.fBounds = &bounds;
2092 }
2093
2094 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002095 LatticePaint latticePaint(paint);
2096 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002097 } else {
2098 this->drawImageRect(image, dst, paint);
2099 }
2100}
2101
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002102static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002103 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002104 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002105 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002106 return SkImage::MakeFromBitmap(bitmap);
2107}
2108
2109void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2110 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002111}
2112
reede47829b2015-08-06 10:02:53 -07002113void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002114 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002115 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002116}
2117
reed84984ef2015-07-17 07:09:43 -07002118void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2119 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002120 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002121}
2122
reede47829b2015-08-06 10:02:53 -07002123void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2124 SrcRectConstraint constraint) {
2125 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2126 constraint);
2127}
reede47829b2015-08-06 10:02:53 -07002128
reed71c3c762015-06-24 10:29:17 -07002129void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002130 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002131 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002132 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002133 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002134 if (count <= 0) {
2135 return;
2136 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002137 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002138 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002139 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002140}
2141
reedf70b5312016-03-04 16:36:20 -08002142void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002143 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002144 if (key) {
2145 this->onDrawAnnotation(rect, key, value);
2146 }
2147}
2148
reede47829b2015-08-06 10:02:53 -07002149void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2150 const SkPaint* paint, SrcRectConstraint constraint) {
2151 if (src) {
2152 this->drawImageRect(image, *src, dst, paint, constraint);
2153 } else {
2154 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2155 dst, paint, constraint);
2156 }
2157}
reede47829b2015-08-06 10:02:53 -07002158
Mike Reed4204da22017-05-17 08:53:36 -04002159void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002160 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002161 this->onDrawShadowRec(path, rec);
2162}
2163
2164void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2165 SkPaint paint;
2166 const SkRect& pathBounds = path.getBounds();
2167
Mike Reed38992392019-07-30 10:48:15 -04002168 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002169 while (iter.next()) {
2170 iter.fDevice->drawShadow(path, rec);
2171 }
Mike Reed38992392019-07-30 10:48:15 -04002172 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002173}
2174
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002175void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002176 QuadAAFlags aaFlags, const SkColor4f& color,
2177 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002178 TRACE_EVENT0("skia", TRACE_FUNC);
2179 // Make sure the rect is sorted before passing it along
2180 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2181}
2182
2183void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2184 const SkPoint dstClips[],
2185 const SkMatrix preViewMatrices[],
2186 const SkPaint* paint,
2187 SrcRectConstraint constraint) {
2188 TRACE_EVENT0("skia", TRACE_FUNC);
2189 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2190}
2191
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192//////////////////////////////////////////////////////////////////////////////
2193// These are the virtual drawing methods
2194//////////////////////////////////////////////////////////////////////////////
2195
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002196void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002197 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002198 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2199 }
2200}
2201
reed41af9662015-01-05 07:49:08 -08002202void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002203 this->internalDrawPaint(paint);
2204}
2205
2206void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002207 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208
2209 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002210 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 }
2212
Mike Reed38992392019-07-30 10:48:15 -04002213 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214}
2215
reed41af9662015-01-05 07:49:08 -08002216void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2217 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218 if ((long)count <= 0) {
2219 return;
2220 }
2221
Mike Reed822128b2017-02-28 16:41:03 -05002222 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002223 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002224 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002225 // special-case 2 points (common for drawing a single line)
2226 if (2 == count) {
2227 r.set(pts[0], pts[1]);
2228 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002229 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002230 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002231 if (!r.isFinite()) {
2232 return;
2233 }
Mike Reed822128b2017-02-28 16:41:03 -05002234 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002235 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2236 return;
2237 }
2238 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002239 }
reed@google.coma584aed2012-05-16 14:06:02 +00002240
halcanary96fcdcc2015-08-27 07:41:13 -07002241 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242
Mike Reed38992392019-07-30 10:48:15 -04002243 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002244
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002246 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247 }
reed@google.com4b226022011-01-11 18:32:13 +00002248
Mike Reed38992392019-07-30 10:48:15 -04002249 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250}
2251
reed4a167172016-08-18 17:15:25 -07002252static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002253 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002254}
2255
reed41af9662015-01-05 07:49:08 -08002256void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002257 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002259 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002260 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002261 return;
2262 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263 }
reed@google.com4b226022011-01-11 18:32:13 +00002264
reed4a167172016-08-18 17:15:25 -07002265 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002266 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002267
reed4a167172016-08-18 17:15:25 -07002268 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002269 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002270 }
2271
Mike Reed38992392019-07-30 10:48:15 -04002272 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002273 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002274 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002275 SkDrawIter iter(this);
2276 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002277 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280}
2281
msarett44df6512016-08-25 13:54:30 -07002282void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002283 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002284 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002285 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002286 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2287 return;
2288 }
msarett44df6512016-08-25 13:54:30 -07002289 }
2290
Mike Reed38992392019-07-30 10:48:15 -04002291 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002292
2293 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002294 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002295 }
2296
Mike Reed38992392019-07-30 10:48:15 -04002297 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002298}
2299
Mike Reedd5674082019-04-19 15:00:47 -04002300void SkCanvas::onDrawBehind(const SkPaint& paint) {
2301 SkIRect bounds;
2302 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2303 for (;;) {
2304 const MCRec* rec = (const MCRec*)iter.prev();
2305 if (!rec) {
2306 return; // no backimages, so nothing to draw
2307 }
2308 if (rec->fBackImage) {
2309 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2310 rec->fBackImage->fImage->width(),
2311 rec->fBackImage->fImage->height());
2312 break;
2313 }
2314 }
2315
Mike Reed38992392019-07-30 10:48:15 -04002316 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002317
2318 while (iter.next()) {
2319 SkBaseDevice* dev = iter.fDevice;
2320
Mike Reedd5674082019-04-19 15:00:47 -04002321 dev->save();
2322 // We use clipRegion because it is already defined to operate in dev-space
2323 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2324 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002325 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002326 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002327 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002328 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002329 }
2330
Mike Reed38992392019-07-30 10:48:15 -04002331 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002332}
2333
reed41af9662015-01-05 07:49:08 -08002334void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002335 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002336 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002337 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002338 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002339 return;
2340 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002341 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002342
Mike Reed38992392019-07-30 10:48:15 -04002343 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002344
2345 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002346 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002347 }
2348
Mike Reed38992392019-07-30 10:48:15 -04002349 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002350}
2351
bsalomonac3aa242016-08-19 11:25:19 -07002352void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2353 SkScalar sweepAngle, bool useCenter,
2354 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002355 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002356 if (paint.canComputeFastBounds()) {
2357 SkRect storage;
2358 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002359 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002360 return;
2361 }
bsalomonac3aa242016-08-19 11:25:19 -07002362 }
2363
Mike Reed38992392019-07-30 10:48:15 -04002364 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002365
2366 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002367 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002368 }
2369
Mike Reed38992392019-07-30 10:48:15 -04002370 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002371}
2372
reed41af9662015-01-05 07:49:08 -08002373void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002374 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002375 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002376 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2377 return;
2378 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002379 }
2380
2381 if (rrect.isRect()) {
2382 // call the non-virtual version
2383 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002384 return;
2385 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002386 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002387 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2388 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002389 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002390
Mike Reed38992392019-07-30 10:48:15 -04002391 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002392
2393 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002394 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002395 }
2396
Mike Reed38992392019-07-30 10:48:15 -04002397 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002398}
2399
Mike Reed822128b2017-02-28 16:41:03 -05002400void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002401 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002402 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002403 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2404 return;
2405 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002406 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002407
Mike Reed38992392019-07-30 10:48:15 -04002408 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002409
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002410 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002411 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002412 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002413
Mike Reed38992392019-07-30 10:48:15 -04002414 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002415}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002416
reed41af9662015-01-05 07:49:08 -08002417void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002418 if (!path.isFinite()) {
2419 return;
2420 }
2421
Mike Reed822128b2017-02-28 16:41:03 -05002422 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002423 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002424 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002425 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2426 return;
2427 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002429
Mike Reed822128b2017-02-28 16:41:03 -05002430 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002431 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002432 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002433 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002434 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002435 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002436
Mike Reed38992392019-07-30 10:48:15 -04002437 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002438
2439 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002440 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441 }
2442
Mike Reed38992392019-07-30 10:48:15 -04002443 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444}
2445
reed262a71b2015-12-05 13:07:27 -08002446bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002447 if (!paint.getImageFilter()) {
2448 return false;
2449 }
2450
2451 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002452 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002453 return false;
2454 }
2455
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002456 // The other paint effects need to be applied before the image filter, but the sprite draw
2457 // applies the filter explicitly first.
2458 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2459 return false;
2460 }
reed262a71b2015-12-05 13:07:27 -08002461 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2462 // Once we can filter and the filter will return a result larger than itself, we should be
2463 // able to remove this constraint.
2464 // skbug.com/4526
2465 //
2466 SkPoint pt;
2467 ctm.mapXY(x, y, &pt);
2468 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2469 return ir.contains(fMCRec->fRasterClip.getBounds());
2470}
2471
Mike Reedf441cfc2018-04-11 14:50:16 -04002472// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2473// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2474// null.
2475static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2476 if (paintParam) {
2477 *real = *paintParam;
2478 real->setStyle(SkPaint::kFill_Style);
2479 real->setPathEffect(nullptr);
2480 paintParam = real;
2481 }
2482 return paintParam;
2483}
2484
reeda85d4d02015-05-06 12:56:48 -07002485void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002486 SkPaint realPaint;
2487 paint = init_image_paint(&realPaint, paint);
2488
reeda85d4d02015-05-06 12:56:48 -07002489 SkRect bounds = SkRect::MakeXYWH(x, y,
2490 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002491 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002492 SkRect tmp = bounds;
2493 if (paint) {
2494 paint->computeFastBounds(tmp, &tmp);
2495 }
2496 if (this->quickReject(tmp)) {
2497 return;
2498 }
reeda85d4d02015-05-06 12:56:48 -07002499 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002500 // At this point we need a real paint object. If the caller passed null, then we should
2501 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2502 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2503 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002504
reeda2217ef2016-07-20 06:04:34 -07002505 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002506 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2507 *paint);
2508 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002509 special = this->getDevice()->makeSpecial(image);
2510 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002511 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002512 }
2513 }
2514
Mike Reed38992392019-07-30 10:48:15 -04002515 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002516
reeda85d4d02015-05-06 12:56:48 -07002517 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002518 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002519 if (special) {
2520 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002521 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002522 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002523 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002524 SkScalarRoundToInt(pt.fY), pnt,
2525 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002526 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002527 iter.fDevice->drawImageRect(
2528 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2529 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002530 }
reeda85d4d02015-05-06 12:56:48 -07002531 }
halcanary9d524f22016-03-29 09:03:52 -07002532
Mike Reed38992392019-07-30 10:48:15 -04002533 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002534}
2535
reed41af9662015-01-05 07:49:08 -08002536void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002537 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002538 SkPaint realPaint;
2539 paint = init_image_paint(&realPaint, paint);
2540
halcanary96fcdcc2015-08-27 07:41:13 -07002541 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002542 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002543 if (paint) {
2544 paint->computeFastBounds(dst, &storage);
2545 }
2546 if (this->quickReject(storage)) {
2547 return;
2548 }
reeda85d4d02015-05-06 12:56:48 -07002549 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002550 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002551
Mike Reed38992392019-07-30 10:48:15 -04002552 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002553
reeda85d4d02015-05-06 12:56:48 -07002554 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002555 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002556 }
halcanary9d524f22016-03-29 09:03:52 -07002557
Mike Reed38992392019-07-30 10:48:15 -04002558 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002559}
2560
reed4c21dc52015-06-25 12:32:03 -07002561void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2562 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002563 SkPaint realPaint;
2564 paint = init_image_paint(&realPaint, paint);
2565
halcanary96fcdcc2015-08-27 07:41:13 -07002566 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002567 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002568 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2569 return;
2570 }
reed@google.com3d608122011-11-21 15:16:16 +00002571 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002572 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002573
Mike Reed38992392019-07-30 10:48:15 -04002574 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002575
reed4c21dc52015-06-25 12:32:03 -07002576 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002577 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002578 }
halcanary9d524f22016-03-29 09:03:52 -07002579
Mike Reed38992392019-07-30 10:48:15 -04002580 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002581}
2582
msarett16882062016-08-16 09:31:08 -07002583void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2584 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002585 SkPaint realPaint;
2586 paint = init_image_paint(&realPaint, paint);
2587
msarett16882062016-08-16 09:31:08 -07002588 if (nullptr == paint || paint->canComputeFastBounds()) {
2589 SkRect storage;
2590 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2591 return;
2592 }
2593 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002594 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002595
Mike Reed38992392019-07-30 10:48:15 -04002596 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002597
2598 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002599 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002600 }
2601
Mike Reed38992392019-07-30 10:48:15 -04002602 DRAW_END
msarett16882062016-08-16 09:31:08 -07002603}
2604
fmalita00d5c2c2014-08-21 08:53:26 -07002605void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2606 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002607 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002608 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002609 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002610 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002611 SkRect tmp;
2612 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2613 return;
2614 }
2615 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002616 }
2617
fmalita024f9962015-03-03 19:08:17 -08002618 // We cannot filter in the looper as we normally do, because the paint is
2619 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002620 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002621
fmalitaaa1b9122014-08-28 14:32:24 -07002622 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002623 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002624 }
2625
Mike Reed38992392019-07-30 10:48:15 -04002626 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002627}
2628
Mike Reed358fcad2018-11-23 15:27:51 -05002629// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002630void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002631 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2632 TRACE_EVENT0("skia", TRACE_FUNC);
2633 if (byteLength) {
2634 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002635 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002636 }
2637}
Mike Reed4f81bb72019-01-23 09:23:00 -05002638
fmalita00d5c2c2014-08-21 08:53:26 -07002639void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2640 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002641 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002642 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002643 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002644 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002645}
reed@google.come0d9ce82014-04-23 04:00:17 +00002646
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002647void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2648 const SkPaint& paint) {
2649 DRAW_BEGIN(paint, nullptr)
2650
2651 while (iter.next()) {
2652 // In the common case of one iteration we could std::move vertices here.
2653 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2654 }
2655
2656 DRAW_END
2657}
Brian Salomon199fb872017-02-06 09:41:10 -05002658
dandovb3c9d1c2014-08-12 08:34:29 -07002659void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002660 const SkPoint texCoords[4], SkBlendMode bmode,
2661 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002662 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002663 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002664 return;
2665 }
mtklein6cfa73a2014-08-13 13:33:49 -07002666
Mike Reedfaba3712016-11-03 14:45:31 -04002667 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002668}
2669
2670void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002671 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002672 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002673 // Since a patch is always within the convex hull of the control points, we discard it when its
2674 // bounding rectangle is completely outside the current clip.
2675 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002676 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002677 if (this->quickReject(bounds)) {
2678 return;
2679 }
mtklein6cfa73a2014-08-13 13:33:49 -07002680
Mike Reed38992392019-07-30 10:48:15 -04002681 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002682
dandovecfff212014-08-04 10:02:00 -07002683 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002684 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002685 }
mtklein6cfa73a2014-08-13 13:33:49 -07002686
Mike Reed38992392019-07-30 10:48:15 -04002687 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002688}
2689
reeda8db7282015-07-07 10:22:31 -07002690void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002691#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002692 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002693#endif
reede3b38ce2016-01-08 09:18:44 -08002694 RETURN_ON_NULL(dr);
2695 if (x || y) {
2696 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2697 this->onDrawDrawable(dr, &matrix);
2698 } else {
2699 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002700 }
2701}
2702
reeda8db7282015-07-07 10:22:31 -07002703void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002704#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002705 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002706#endif
reede3b38ce2016-01-08 09:18:44 -08002707 RETURN_ON_NULL(dr);
2708 if (matrix && matrix->isIdentity()) {
2709 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002710 }
reede3b38ce2016-01-08 09:18:44 -08002711 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002712}
2713
2714void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002715 // drawable bounds are no longer reliable (e.g. android displaylist)
2716 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002717 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002718}
2719
reed71c3c762015-06-24 10:29:17 -07002720void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002721 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002722 const SkRect* cull, const SkPaint* paint) {
2723 if (cull && this->quickReject(*cull)) {
2724 return;
2725 }
2726
2727 SkPaint pnt;
2728 if (paint) {
2729 pnt = *paint;
2730 }
halcanary9d524f22016-03-29 09:03:52 -07002731
Mike Reed38992392019-07-30 10:48:15 -04002732 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002733 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002734 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002735 }
Mike Reed38992392019-07-30 10:48:15 -04002736 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002737}
2738
reedf70b5312016-03-04 16:36:20 -08002739void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2740 SkASSERT(key);
2741
2742 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002743 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002744 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002745 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002746 }
Mike Reed38992392019-07-30 10:48:15 -04002747 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002748}
2749
Michael Ludwiga595f862019-08-27 15:25:49 -04002750void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2751 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002752 SkASSERT(r.isSorted());
2753
2754 // If this used a paint, it would be a filled color with blend mode, which does not
2755 // need to use an autodraw loop, so use SkDrawIter directly.
2756 if (this->quickReject(r)) {
2757 return;
2758 }
2759
Michael Ludwiga4b44882019-08-28 14:34:58 -04002760 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002761 SkDrawIter iter(this);
2762 while(iter.next()) {
2763 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2764 }
2765}
2766
2767void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2768 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2769 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002770 if (count <= 0) {
2771 // Nothing to draw
2772 return;
2773 }
2774
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002775 SkPaint realPaint;
2776 init_image_paint(&realPaint, paint);
2777
Michael Ludwiga4b44882019-08-28 14:34:58 -04002778 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2779 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2780 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2781 // or we need it for the autolooper (since it greatly improves image filter perf).
2782 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2783 bool setBoundsValid = count == 1 || needsAutoLooper;
2784 SkRect setBounds = imageSet[0].fDstRect;
2785 if (imageSet[0].fMatrixIndex >= 0) {
2786 // Account for the per-entry transform that is applied prior to the CTM when drawing
2787 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002788 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002789 if (needsAutoLooper) {
2790 for (int i = 1; i < count; ++i) {
2791 SkRect entryBounds = imageSet[i].fDstRect;
2792 if (imageSet[i].fMatrixIndex >= 0) {
2793 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2794 }
2795 setBounds.joinPossiblyEmptyRect(entryBounds);
2796 }
2797 }
2798
2799 // If we happen to have the draw bounds, though, might as well check quickReject().
2800 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2801 SkRect tmp;
2802 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2803 return;
2804 }
2805 }
2806
2807 if (needsAutoLooper) {
2808 SkASSERT(setBoundsValid);
2809 DRAW_BEGIN(realPaint, &setBounds)
2810 while (iter.next()) {
2811 iter.fDevice->drawEdgeAAImageSet(
2812 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2813 }
2814 DRAW_END
2815 } else {
2816 this->predrawNotify();
2817 SkDrawIter iter(this);
2818 while(iter.next()) {
2819 iter.fDevice->drawEdgeAAImageSet(
2820 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2821 }
2822 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002823}
2824
reed@android.com8a1c16f2008-12-17 15:59:43 +00002825//////////////////////////////////////////////////////////////////////////////
2826// These methods are NOT virtual, and therefore must call back into virtual
2827// methods, rather than actually drawing themselves.
2828//////////////////////////////////////////////////////////////////////////////
2829
reed374772b2016-10-05 17:33:02 -07002830void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002832 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002833 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002834 this->drawPaint(paint);
2835}
2836
2837void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002838 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002839 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2840}
2841
Mike Reed3661bc92017-02-22 13:21:42 -05002842void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002843 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002844 pts[0].set(x0, y0);
2845 pts[1].set(x1, y1);
2846 this->drawPoints(kLines_PointMode, 2, pts, paint);
2847}
2848
Mike Reed3661bc92017-02-22 13:21:42 -05002849void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002850 if (radius < 0) {
2851 radius = 0;
2852 }
2853
2854 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002855 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002856 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002857}
2858
2859void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2860 const SkPaint& paint) {
2861 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002862 SkRRect rrect;
2863 rrect.setRectXY(r, rx, ry);
2864 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 } else {
2866 this->drawRect(r, paint);
2867 }
2868}
2869
reed@android.com8a1c16f2008-12-17 15:59:43 +00002870void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2871 SkScalar sweepAngle, bool useCenter,
2872 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002873 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002874 if (oval.isEmpty() || !sweepAngle) {
2875 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002876 }
bsalomon21af9ca2016-08-25 12:29:23 -07002877 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002878}
2879
reed@android.comf76bacf2009-05-13 14:00:33 +00002880///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002881#ifdef SK_DISABLE_SKPICTURE
2882void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002883
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002884
2885void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2886 const SkPaint* paint) {}
2887#else
Mike Klein88d90712018-01-27 17:30:04 +00002888/**
2889 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2890 * against the playback cost of recursing into the subpicture to get at its actual ops.
2891 *
2892 * For now we pick a conservatively small value, though measurement (and other heuristics like
2893 * the type of ops contained) may justify changing this value.
2894 */
2895#define kMaxPictureOpsToUnrollInsteadOfRef 1
2896
reedd5fa1a42014-08-09 11:08:05 -07002897void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002898 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002899 RETURN_ON_NULL(picture);
2900
reede3b38ce2016-01-08 09:18:44 -08002901 if (matrix && matrix->isIdentity()) {
2902 matrix = nullptr;
2903 }
Mike Klein88d90712018-01-27 17:30:04 +00002904 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2905 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2906 picture->playback(this);
2907 } else {
2908 this->onDrawPicture(picture, matrix, paint);
2909 }
reedd5fa1a42014-08-09 11:08:05 -07002910}
robertphillips9b14f262014-06-04 05:40:44 -07002911
reedd5fa1a42014-08-09 11:08:05 -07002912void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2913 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002914 if (!paint || paint->canComputeFastBounds()) {
2915 SkRect bounds = picture->cullRect();
2916 if (paint) {
2917 paint->computeFastBounds(bounds, &bounds);
2918 }
2919 if (matrix) {
2920 matrix->mapRect(&bounds);
2921 }
2922 if (this->quickReject(bounds)) {
2923 return;
2924 }
2925 }
2926
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002927 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002928 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002929}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002930#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002931
reed@android.com8a1c16f2008-12-17 15:59:43 +00002932///////////////////////////////////////////////////////////////////////////////
2933///////////////////////////////////////////////////////////////////////////////
2934
reed3aafe112016-08-18 12:45:34 -07002935SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002936 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002937
2938 SkASSERT(canvas);
2939
reed3aafe112016-08-18 12:45:34 -07002940 fImpl = new (fStorage) SkDrawIter(canvas);
Michael Ludwig2a8a3ff2020-03-26 15:33:20 -04002941 // This advances the base iterator to the first device and caches its origin,
2942 // correctly handling the case where there are no devices.
2943 this->next();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944}
2945
2946SkCanvas::LayerIter::~LayerIter() {
2947 fImpl->~SkDrawIter();
2948}
2949
2950void SkCanvas::LayerIter::next() {
2951 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002952 if (!fDone) {
2953 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2954 // filters, so its devices will always be able to report the origin exactly.
2955 fDeviceOrigin = fImpl->fDevice->getOrigin();
2956 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002957}
2958
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002959SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002960 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002961}
2962
2963const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002964 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002965}
2966
2967const SkPaint& SkCanvas::LayerIter::paint() const {
2968 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002969 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002970 paint = &fDefaultPaint;
2971 }
2972 return *paint;
2973}
2974
Mike Reedca37f322018-03-08 13:22:16 -05002975SkIRect SkCanvas::LayerIter::clipBounds() const {
2976 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002977}
2978
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002979int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2980int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002981
2982///////////////////////////////////////////////////////////////////////////////
2983
Brian Osmane8a98632019-04-10 10:26:10 -04002984SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2985SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2986SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2987SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
2988
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002989SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2990 const SkRect& dstRect, int matrixIndex, float alpha,
2991 unsigned aaFlags, bool hasClip)
2992 : fImage(std::move(image))
2993 , fSrcRect(srcRect)
2994 , fDstRect(dstRect)
2995 , fMatrixIndex(matrixIndex)
2996 , fAlpha(alpha)
2997 , fAAFlags(aaFlags)
2998 , fHasClip(hasClip) {}
2999
3000SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3001 const SkRect& dstRect, float alpha, unsigned aaFlags)
3002 : fImage(std::move(image))
3003 , fSrcRect(srcRect)
3004 , fDstRect(dstRect)
3005 , fAlpha(alpha)
3006 , fAAFlags(aaFlags) {}
3007
3008///////////////////////////////////////////////////////////////////////////////
3009
Mike Reed5df49342016-11-12 08:06:55 -06003010std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003011 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003012 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003013 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003014 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003015
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003016 SkBitmap bitmap;
3017 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003018 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003019 }
Mike Reed12f77342017-11-08 11:19:52 -05003020
3021 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003022 std::make_unique<SkCanvas>(bitmap, *props) :
3023 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003024}
reedd5fa1a42014-08-09 11:08:05 -07003025
3026///////////////////////////////////////////////////////////////////////////////
3027
Florin Malitaee424ac2016-12-01 12:47:59 -05003028SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003029 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003030
Florin Malita439ace92016-12-02 12:05:41 -05003031SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003032 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003033
Herb Derbyefe39bc2018-05-01 17:06:20 -04003034SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003035 : INHERITED(device) {}
3036
Florin Malitaee424ac2016-12-01 12:47:59 -05003037SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3038 (void)this->INHERITED::getSaveLayerStrategy(rec);
3039 return kNoLayer_SaveLayerStrategy;
3040}
3041
Mike Reed148b7fd2018-12-18 17:38:18 -05003042bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3043 return false;
3044}
3045
Florin Malitaee424ac2016-12-01 12:47:59 -05003046///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003047
reed73603f32016-09-20 08:42:38 -07003048static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3049static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3050static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3051static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3052static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3053static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003054
3055///////////////////////////////////////////////////////////////////////////////////////////////////
3056
3057SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3058 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003059 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003060 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003061 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003062 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003063 clip.setEmpty();
3064 }
3065
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003066 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003067 return handle;
3068 }
3069 return nullptr;
3070}
3071
3072static bool install(SkBitmap* bm, const SkImageInfo& info,
3073 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003074 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003075}
3076
3077SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3078 SkBitmap* bm) {
3079 SkRasterHandleAllocator::Rec rec;
3080 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3081 return nullptr;
3082 }
3083 return rec.fHandle;
3084}
3085
3086std::unique_ptr<SkCanvas>
3087SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3088 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003089 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003090 return nullptr;
3091 }
3092
3093 SkBitmap bm;
3094 Handle hndl;
3095
3096 if (rec) {
3097 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3098 } else {
3099 hndl = alloc->allocBitmap(info, &bm);
3100 }
3101 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3102}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003103
3104///////////////////////////////////////////////////////////////////////////////////////////////////