blob: d905a68c14a0c3683a6060f992242f3264d5cd38 [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
737void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800738 if (fMCRec->fDeferredSaveCount > 0) {
739 SkASSERT(fSaveCount > 1);
740 fSaveCount -= 1;
741 fMCRec->fDeferredSaveCount -= 1;
742 } else {
743 // check for underflow
744 if (fMCStack.count() > 1) {
745 this->willRestore();
746 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700747 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800748 this->internalRestore();
749 this->didRestore();
750 }
reedf0090cb2014-11-26 08:55:51 -0800751 }
752}
753
754void SkCanvas::restoreToCount(int count) {
755 // sanity check
756 if (count < 1) {
757 count = 1;
758 }
mtkleinf0f14112014-12-12 08:46:25 -0800759
reedf0090cb2014-11-26 08:55:51 -0800760 int n = this->getSaveCount() - count;
761 for (int i = 0; i < n; ++i) {
762 this->restore();
763 }
764}
765
reed2ff1fce2014-12-11 07:07:37 -0800766void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700768 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000770
Mike Reedc42a1cd2017-02-14 14:25:14 -0500771 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772}
773
reed4960eee2015-12-18 07:09:18 -0800774bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400775 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776}
777
reed4960eee2015-12-18 07:09:18 -0800778bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700779 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400780 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
781 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
782 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
783 // filter does not require a decomposed CTM matrix, the filter space and device space are the
784 // same. When it has been decomposed, we want the original image filter node to process the
785 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
786 // longer the original filter, but has the remainder matrix baked into it, and passing in the
787 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
788 // to the original filter node (barring inflation from consecutive calls to mapRect). While
789 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
790 // passing getDeviceClipBounds() to 'imageFilter' is correct.
791 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
792 // be important to more accurately calculate the clip bounds in the layer space for the original
793 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500794 SkIRect clipBounds = this->getDeviceClipBounds();
795 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000796 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000797 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000798
reed96e657d2015-03-10 17:30:07 -0700799 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
800
Robert Phillips12078432018-05-17 11:17:39 -0400801 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
802 // If the image filter DAG affects transparent black then we will need to render
803 // out to the clip bounds
804 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000805 }
Robert Phillips12078432018-05-17 11:17:39 -0400806
807 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700808 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700810 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400811 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400813 inputSaveLayerBounds = clipBounds;
814 }
815
816 if (imageFilter) {
817 // expand the clip bounds by the image filter DAG to include extra content that might
818 // be required by the image filters.
819 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
820 SkImageFilter::kReverse_MapDirection,
821 &inputSaveLayerBounds);
822 }
823
824 SkIRect clippedSaveLayerBounds;
825 if (bounds) {
826 // For better or for worse, user bounds currently act as a hard clip on the layer's
827 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
828 clippedSaveLayerBounds = inputSaveLayerBounds;
829 } else {
830 // If there are no user bounds, we don't want to artificially restrict the resulting
831 // layer bounds, so allow the expanded clip bounds free reign.
832 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800834
835 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400836 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800837 if (BoundsAffectsClip(saveLayerFlags)) {
838 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
839 fMCRec->fRasterClip.setEmpty();
840 fDeviceClipBounds.setEmpty();
841 }
842 return false;
843 }
Robert Phillips12078432018-05-17 11:17:39 -0400844 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845
reed4960eee2015-12-18 07:09:18 -0800846 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700847 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400848 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
849 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000850 }
851
852 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400853 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000854 }
Robert Phillips12078432018-05-17 11:17:39 -0400855
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856 return true;
857}
858
reed4960eee2015-12-18 07:09:18 -0800859int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
860 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000861}
862
Cary Clarke041e312018-03-06 13:00:52 -0500863int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700864 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400865 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
866 // no need for the layer (or any of the draws until the matching restore()
867 this->save();
868 this->clipRect({0,0,0,0});
869 } else {
870 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
871 fSaveCount += 1;
872 this->internalSaveLayer(rec, strategy);
873 }
reed4960eee2015-12-18 07:09:18 -0800874 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800875}
876
Mike Reed148b7fd2018-12-18 17:38:18 -0500877int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
878 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
879 // Assuming clips never expand, if the request bounds is outside of the current clip
880 // there is no need to copy/restore the area, so just devolve back to a regular save.
881 this->save();
882 } else {
883 bool doTheWork = this->onDoSaveBehind(bounds);
884 fSaveCount += 1;
885 this->internalSave();
886 if (doTheWork) {
887 this->internalSaveBehind(bounds);
888 }
889 }
890 return this->getSaveCount() - 1;
891}
892
reeda2217ef2016-07-20 06:04:34 -0700893void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500894 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500895 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400896 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
897 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400898 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500899 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
900 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400901 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400902 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
903 // This means that we only have to copy a dst-sized block of pixels out of src and translate
904 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400905 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
906 dstOrigin.y() - src->getOrigin().y(),
907 dst->width(), dst->height());
908 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400909 return;
910 }
911
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400912 auto special = src->snapSpecial(snapBounds);
913 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400914 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
915 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400916 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
917 }
918 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400919 }
920
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400921 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
922 // by the backdrop filter.
923 SkMatrix toRoot, layerMatrix;
924 SkSize scale;
925 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
926 toRoot = SkMatrix::I();
927 layerMatrix = ctm;
928 } else if (ctm.decomposeScale(&scale, &toRoot)) {
929 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
930 } else {
931 // Perspective, for now, do no scaling of the layer itself.
932 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
933 // the matrix, e.g. based on the midpoint of the near/far planes?
934 toRoot = ctm;
935 layerMatrix = SkMatrix::I();
936 }
937
938 // We have to map the dst bounds from the root space into the layer space where filtering will
939 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
940 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
941 // is a safe, conservative estimate.
942 SkMatrix fromRoot;
943 if (!toRoot.invert(&fromRoot)) {
944 return;
945 }
946
947 // This represents what the backdrop filter needs to produce in the layer space, and is sized
948 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
949 SkIRect layerTargetBounds = fromRoot.mapRect(
950 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
951 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
952 // require some extra input pixels.
953 SkIRect layerInputBounds = filter->filterBounds(
954 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
955 &layerTargetBounds);
956
957 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400958 // be the conservative contents required to fill a layerInputBounds-sized surface with the
959 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400960 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
961 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
962 if (!backdropBounds.intersect(srcDevRect)) {
963 return;
964 }
965
966 auto special = src->snapSpecial(backdropBounds);
967 if (!special) {
968 return;
969 }
970
971 SkColorType colorType = src->imageInfo().colorType();
972 if (colorType == kUnknown_SkColorType) {
973 colorType = kRGBA_8888_SkColorType;
974 }
975 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400976
977 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400978 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400979 // Drawing the temporary and final filtered image requires a higher filter quality if the
980 // 'toRoot' transformation is not identity, in order to minimize the impact on already
981 // rendered edges/content.
982 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
983 p.setFilterQuality(kHigh_SkFilterQuality);
984
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400985 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
986 // and stored in a temporary surface, which is then used as the input to the actual filter.
987 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
988 if (!tmpSurface) {
989 return;
990 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400991
992 auto tmpCanvas = tmpSurface->getCanvas();
993 tmpCanvas->clear(SK_ColorTRANSPARENT);
994 // Reading in reverse, this takes the backdrop bounds from src device space into the root
995 // space, then maps from root space into the layer space, then maps it so the input layer's
996 // top left corner is (0, 0). This transformation automatically accounts for any cropping
997 // performed on backdropBounds.
998 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
999 tmpCanvas->concat(fromRoot);
1000 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001001
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001002 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1003 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1004 special = tmpSurface->makeImageSnapshot();
1005 } else {
1006 // Since there is no extra transform that was done, update the input bounds to reflect
1007 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1008 // was equal to backdropBounds before it was made relative to the src device and cropped.
1009 // When we use the original snapped image directly, just map the update backdrop bounds
1010 // back into the shared layer space
1011 layerInputBounds = backdropBounds;
1012 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001013
1014 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1015 // draw will be 1-1 so there is no need to increase filter quality.
1016 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001017 }
1018
1019 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1020 // layer space. This has to further offset everything so that filter evaluation thinks the
1021 // source image's top left corner is (0, 0).
1022 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1023 // this can be simplified.
1024 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1025 SkMatrix filterCTM = layerMatrix;
1026 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1027 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1028
1029 SkIPoint offset;
1030 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001031 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001032 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1033 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1034 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1035 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001036 offset += layerInputBounds.topLeft();
1037
1038 // Manually setting the device's CTM requires accounting for the device's origin.
1039 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001040 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001041 // a global CTM instead of a device CTM.
1042 SkMatrix dstCTM = toRoot;
1043 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001044 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001045
1046 // And because devices don't have a special-image draw function that supports arbitrary
1047 // matrices, we are abusing the asImage() functionality here...
1048 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001049 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001051 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001052 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1053 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001054 }
robertphillips7354a4b2015-12-16 05:08:27 -08001055}
reed70ee31b2015-12-10 13:44:45 -08001056
Mike Kleine083f7c2018-02-07 12:54:27 -05001057static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001058 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001059 if (prev.bytesPerPixel() <= 4 &&
1060 prev.colorType() != kRGBA_8888_SkColorType &&
1061 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001062 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1063 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1064 ct = kN32_SkColorType;
1065 }
1066 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001067}
1068
reed4960eee2015-12-18 07:09:18 -08001069void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001070 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001071 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001072 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1073
Michael Ludwigeced98b2020-03-03 10:39:41 -05001074 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1075 // saveLayer ignores mask filters, so force it to null
1076 if (paint.get() && paint->getMaskFilter()) {
1077 paint.writable()->setMaskFilter(nullptr);
1078 }
1079
Mike Reed5532c2a2019-02-23 12:00:32 -05001080 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1081 // regardless of any hint-rect from the caller. skbug.com/8783
1082 if (rec.fBackdrop) {
1083 bounds = nullptr;
1084 }
1085
Michael Ludwigeced98b2020-03-03 10:39:41 -05001086 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001087 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001088 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001089
reed8c30a812016-04-20 16:36:51 -07001090 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001091 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1092 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1093 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001094 *
1095 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001096 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1097 * if necessary.
1098 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1099 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001100 * 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 -04001101 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001102 * of the original imagefilter, and draw that (via drawSprite)
1103 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1104 *
1105 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1106 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1107 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001108 if (imageFilter) {
1109 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001110 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1111 &modifiedCTM);
1112 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001113 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001114 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001115 modifiedRec = fMCRec;
1116 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001117 imageFilter = modifiedFilter.get();
1118 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001119 }
1120 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1121 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001122 }
reed8c30a812016-04-20 16:36:51 -07001123
junov@chromium.orga907ac32012-02-24 21:54:07 +00001124 // do this before we create the layer. We don't call the public save() since
1125 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001126 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001127
junov@chromium.orga907ac32012-02-24 21:54:07 +00001128 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001129 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001130 if (modifiedRec) {
1131 // In this case there will be no layer in which to stash the matrix so we need to
1132 // revert the prior MCRec to its earlier state.
1133 modifiedRec->fMatrix = stashedMatrix;
1134 }
reed2ff1fce2014-12-11 07:07:37 -08001135 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 }
1137
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001138 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1139 // the clipRectBounds() call above?
1140 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001141 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001142 }
1143
reed8dc0ccb2015-03-20 06:32:52 -07001144 SkPixelGeometry geo = fProps.pixelGeometry();
1145 if (paint) {
reed76033be2015-03-14 10:54:31 -07001146 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001147 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001148 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001149 }
1150 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151
robertphillips5139e502016-07-19 05:10:40 -07001152 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001153 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001154 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001155 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001156 }
reedb2db8982014-11-13 12:41:02 -08001157
Mike Kleine083f7c2018-02-07 12:54:27 -05001158 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001159 if (rec.fSaveLayerFlags & kF16ColorType) {
1160 info = info.makeColorType(kRGBA_F16_SkColorType);
1161 }
reed129ed1c2016-02-22 06:42:31 -08001162
Hal Canary704cd322016-11-07 14:13:52 -05001163 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001164 {
Florin Malita4571e492019-07-16 10:25:58 -04001165 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001166 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001167 const bool trackCoverage =
1168 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001169 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001170 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001171 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001172 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1173 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001174 return;
reed61f501f2015-04-29 08:34:00 -07001175 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001176 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001177 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178
Mike Reedb43a3e02017-02-11 10:18:58 -05001179 // only have a "next" if this new layer doesn't affect the clip (rare)
1180 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 fMCRec->fLayer = layer;
1182 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001183
Mike Reedc61abee2017-02-28 17:45:27 -05001184 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001185 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001186 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001187 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001188
Mike Reedc42a1cd2017-02-14 14:25:14 -05001189 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1190
1191 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1192 if (layer->fNext) {
1193 // need to punch a hole in the previous device, so we don't draw there, given that
1194 // the new top-layer will allow drawing to happen "below" it.
1195 SkRegion hole(ir);
1196 do {
1197 layer = layer->fNext;
1198 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1199 } while (layer->fNext);
1200 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201}
1202
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001203int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001204 if (0xFF == alpha) {
1205 return this->saveLayer(bounds, nullptr);
1206 } else {
1207 SkPaint tmpPaint;
1208 tmpPaint.setAlpha(alpha);
1209 return this->saveLayer(bounds, &tmpPaint);
1210 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001211}
1212
Mike Reed148b7fd2018-12-18 17:38:18 -05001213void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001214 SkBaseDevice* device = this->getTopDevice();
1215 if (nullptr == device) { // Do we still need this check???
1216 return;
1217 }
1218
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001219 // Map the local bounds into the top device's coordinate space (this is not
1220 // necessarily the full global CTM transform).
1221 SkIRect devBounds;
1222 if (localBounds) {
1223 SkRect tmp;
1224 device->localToDevice().mapRect(&tmp, *localBounds);
1225 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1226 devBounds.setEmpty();
1227 }
1228 } else {
1229 devBounds = device->devClipBounds();
1230 }
1231 if (devBounds.isEmpty()) {
1232 return;
1233 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001234
Michael Ludwigac352122019-08-28 21:03:05 +00001235 // This is getting the special image from the current device, which is then drawn into (both by
1236 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1237 // own device, we need to explicitly copy the back image contents so that its original content
1238 // is available when we splat it back later during restore.
1239 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001240 if (!backImage) {
1241 return;
1242 }
1243
1244 // we really need the save, so we can wack the fMCRec
1245 this->checkForDeferredSave();
1246
1247 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1248
1249 SkPaint paint;
1250 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001251 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001252}
1253
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254void SkCanvas::internalRestore() {
1255 SkASSERT(fMCStack.count() != 0);
1256
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001257 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 DeviceCM* layer = fMCRec->fLayer; // may be null
1259 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001260 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261
Mike Reed148b7fd2018-12-18 17:38:18 -05001262 // move this out before we do the actual restore
1263 auto backImage = std::move(fMCRec->fBackImage);
1264
Mike Reed7fe6ee32020-04-09 12:35:09 -04001265 while (!fMarkerStack.empty() && fMarkerStack.back().fMCRec == fMCRec) {
1266 fMarkerStack.pop_back();
Mike Reedb18e74d2020-01-16 13:58:22 -05001267 }
1268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 // now do the normal restore()
1270 fMCRec->~MCRec(); // balanced in save()
1271 fMCStack.pop_back();
1272 fMCRec = (MCRec*)fMCStack.back();
1273
Mike Reedc42a1cd2017-02-14 14:25:14 -05001274 if (fMCRec) {
1275 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1276 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001277
Mike Reed148b7fd2018-12-18 17:38:18 -05001278 if (backImage) {
1279 SkPaint paint;
1280 paint.setBlendMode(SkBlendMode::kDstOver);
1281 const int x = backImage->fLoc.x();
1282 const int y = backImage->fLoc.y();
1283 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1284 nullptr, SkMatrix::I());
1285 }
1286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1288 since if we're being recorded, we don't want to record this (the
1289 recorder will have already recorded the restore).
1290 */
bsalomon49f085d2014-09-05 13:34:00 -07001291 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001292 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001293 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001294 // At this point, 'layer' has been removed from the device stack, so the devices that
1295 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1296 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001297 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001298 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001299 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001300 delete layer;
reedb679ca82015-04-07 04:40:48 -07001301 } else {
1302 // we're at the root
reeda499f902015-05-01 09:34:31 -07001303 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001304 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001305 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001307 }
msarettfbfa2582016-08-12 08:29:08 -07001308
1309 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001310 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001311 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1312 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313}
1314
reede8f30622016-03-23 18:59:25 -07001315sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001316 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001317 props = &fProps;
1318 }
1319 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001320}
1321
reede8f30622016-03-23 18:59:25 -07001322sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001323 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001324 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001325}
1326
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001327SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001328 return this->onImageInfo();
1329}
1330
1331SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001332 SkBaseDevice* dev = this->getDevice();
1333 if (dev) {
1334 return dev->imageInfo();
1335 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001336 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001337 }
1338}
1339
brianosman898235c2016-04-06 07:38:23 -07001340bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001341 return this->onGetProps(props);
1342}
1343
1344bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001345 SkBaseDevice* dev = this->getDevice();
1346 if (dev) {
1347 if (props) {
1348 *props = fProps;
1349 }
1350 return true;
1351 } else {
1352 return false;
1353 }
1354}
1355
reed6ceeebd2016-03-09 14:26:26 -08001356bool SkCanvas::peekPixels(SkPixmap* pmap) {
1357 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001358}
1359
reed884e97c2015-05-26 11:31:54 -07001360bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001361 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001362 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001363}
1364
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001365void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001366 SkPixmap pmap;
1367 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001368 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001369 }
1370 if (info) {
1371 *info = pmap.info();
1372 }
1373 if (rowBytes) {
1374 *rowBytes = pmap.rowBytes();
1375 }
1376 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001377 // If the caller requested the origin, they presumably are expecting the returned pixels to
1378 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1379 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1380 // instead of an origin, just don't expose the pixels in that case. Note that this means
1381 // that layers with complex coordinate spaces can still report their pixels if the caller
1382 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1383 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1384 *origin = this->getTopDevice()->getOrigin();
1385 } else {
1386 return nullptr;
1387 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001388 }
reed884e97c2015-05-26 11:31:54 -07001389 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001390}
1391
reed884e97c2015-05-26 11:31:54 -07001392bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001393 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001394 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001395}
1396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398
Mike Reed8bcd1282019-03-13 16:51:54 -04001399// In our current design/features, we should never have a layer (src) in a different colorspace
1400// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1401// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1402// colorspace.
1403static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1404 SkASSERT(src == dst);
1405}
1406
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001407void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001408 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001410 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 paint = &tmp;
1412 }
reed@google.com4b226022011-01-11 18:32:13 +00001413
Mike Reed38992392019-07-30 10:48:15 -04001414 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001415
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001417 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001418 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1419 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001420 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001421 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001422 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1423 // so it should always be possible to use the relative origin. Once drawDevice() and
1424 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1425 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001426 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001427 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1428 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001429 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1430 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001431 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1432 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001433 }
reed@google.com76dd2772012-01-05 21:15:07 +00001434 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001435 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001436 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 }
reeda2217ef2016-07-20 06:04:34 -07001438
Mike Reed38992392019-07-30 10:48:15 -04001439 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440}
1441
reed32704672015-12-16 08:27:10 -08001442/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001443
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001444void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001445 if (dx || dy) {
1446 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001447 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001448
reedfe69b502016-09-12 06:31:48 -07001449 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001450 // However, if either is non-finite, we might still complicate the matrix type,
1451 // so we still have to compute this.
1452 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001453
Mike Reedc42a1cd2017-02-14 14:25:14 -05001454 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001455
reedfe69b502016-09-12 06:31:48 -07001456 this->didTranslate(dx,dy);
1457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458}
1459
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001460void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001461 if (sx != 1 || sy != 1) {
1462 this->checkForDeferredSave();
1463 fMCRec->fMatrix.preScale(sx, sy);
1464
1465 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1466 // but pre-scaling by a non-finite does change it, so we have to recompute.
1467 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1468
1469 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1470
1471 this->didScale(sx, sy);
1472 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473}
1474
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001475void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001476 SkMatrix m;
1477 m.setRotate(degrees);
1478 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
bungeman7438bfc2016-07-12 15:01:19 -07001481void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1482 SkMatrix m;
1483 m.setRotate(degrees, px, py);
1484 this->concat(m);
1485}
1486
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001487void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001488 SkMatrix m;
1489 m.setSkew(sx, sy);
1490 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001491}
1492
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001493void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001494 if (matrix.isIdentity()) {
1495 return;
1496 }
1497
reed2ff1fce2014-12-11 07:07:37 -08001498 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001499 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001500
msarett9637ea92016-08-18 14:03:30 -07001501 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001502
Mike Reed7627fa52017-02-08 10:07:53 -05001503 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001504
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001506}
1507
Mike Reed2f92c932020-04-06 15:44:17 -04001508void SkCanvas::internalConcat44(const SkM44& m) {
Mike Reed403c8072020-01-08 10:40:39 -05001509 this->checkForDeferredSave();
1510
Mike Reed3ef77dd2020-04-06 10:41:09 -04001511 fMCRec->fMatrix.preConcat(m);
Mike Reed403c8072020-01-08 10:40:39 -05001512
1513 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1514
1515 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed2f92c932020-04-06 15:44:17 -04001516}
Mike Reed403c8072020-01-08 10:40:39 -05001517
Mike Reed2f92c932020-04-06 15:44:17 -04001518void SkCanvas::concat(const SkM44& m) {
1519 this->internalConcat44(m);
Mike Reeda735ad92020-04-06 21:32:43 -04001520 // notify subclasses
1521#ifdef SK_SUPPORT_LEGACY_DIDCONCAT44
1522 this->didConcat44(SkMatrixPriv::M44ColMajor(m));
1523#else
1524 this->didConcat44(m);
1525#endif
Mike Reedee3216d2020-01-17 17:35:04 -05001526}
1527
reed8c30a812016-04-20 16:36:51 -07001528void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001529 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001530 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001531
Mike Reedc42a1cd2017-02-14 14:25:14 -05001532 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001533}
1534
1535void SkCanvas::setMatrix(const SkMatrix& matrix) {
1536 this->checkForDeferredSave();
1537 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001538 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539}
1540
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001542 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543}
1544
Mike Reed7fe6ee32020-04-09 12:35:09 -04001545void SkCanvas::markCTM(MarkerID id) {
1546 if (id == 0) return;
1547
1548 this->onMarkCTM(id);
1549
1550 SkM44 mx = this->getLocalToDevice();
1551
1552 // Look if we've already seen id in this save-frame.
1553 // If so, replace, else append
1554 for (int i = fMarkerStack.size() - 1; i >= 0; --i) {
1555 auto& m = fMarkerStack[i];
1556 if (m.fMCRec != fMCRec) { // we've gone past the current save-frame
1557 break; // fall out so we append
1558 }
1559 if (m.fID == id) { // in current frame, so replace
1560 m.fMatrix = mx;
1561 return;
1562 }
1563 }
1564 // if we get here, we should append a new marker
1565 fMarkerStack.push_back({fMCRec, mx, id});
1566}
1567
1568bool SkCanvas::findMarkedCTM(MarkerID id, SkM44* mx) const {
1569 // search from top to bottom, so we find the most recent id
1570 for (int i = fMarkerStack.size() - 1; i >= 0; --i) {
1571 if (fMarkerStack[i].fID == id) {
1572 if (mx) {
1573 *mx = fMarkerStack[i].fMatrix;
1574 }
1575 return true;
1576 }
1577 }
1578 return false;
1579}
1580
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581//////////////////////////////////////////////////////////////////////////////
1582
Mike Reedc1f77742016-12-09 09:00:50 -05001583void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001584 if (!rect.isFinite()) {
1585 return;
1586 }
reed2ff1fce2014-12-11 07:07:37 -08001587 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001588 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1589 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001590}
1591
Mike Reedc1f77742016-12-09 09:00:50 -05001592void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001593 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001594
Mike Reed7627fa52017-02-08 10:07:53 -05001595 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001596
reedc64eff52015-11-21 12:39:45 -08001597 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001598 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1599 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001600 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601}
1602
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001603void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1604 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001605 if (fClipRestrictionRect.isEmpty()) {
1606 // we notify the device, but we *dont* resolve deferred saves (since we're just
1607 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001608 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001609 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001610 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001611 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001612 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001613 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001614 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1615 }
1616}
1617
Mike Reedc1f77742016-12-09 09:00:50 -05001618void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001619 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001620 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001621 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1623 } else {
1624 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001625 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001627
Mike Reedc1f77742016-12-09 09:00:50 -05001628void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001629 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001630
Brian Salomona3b45d42016-10-03 11:36:16 -04001631 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001632
Mike Reed7627fa52017-02-08 10:07:53 -05001633 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001634
Mike Reed20800c82017-11-15 16:09:04 -05001635 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1636 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001637 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001638}
1639
Mike Reedc1f77742016-12-09 09:00:50 -05001640void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001641 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001642 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001643
1644 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1645 SkRect r;
1646 if (path.isRect(&r)) {
1647 this->onClipRect(r, op, edgeStyle);
1648 return;
1649 }
1650 SkRRect rrect;
1651 if (path.isOval(&r)) {
1652 rrect.setOval(r);
1653 this->onClipRRect(rrect, op, edgeStyle);
1654 return;
1655 }
1656 if (path.isRRect(&rrect)) {
1657 this->onClipRRect(rrect, op, edgeStyle);
1658 return;
1659 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001660 }
robertphillips39f05382015-11-24 09:30:12 -08001661
1662 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001663}
1664
Mike Reedc1f77742016-12-09 09:00:50 -05001665void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001666 AutoValidateClip avc(this);
1667
Brian Salomona3b45d42016-10-03 11:36:16 -04001668 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001669
Mike Reed7627fa52017-02-08 10:07:53 -05001670 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671
Brian Salomona3b45d42016-10-03 11:36:16 -04001672 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001673 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001674 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001675 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676}
1677
Mike Reed121c2af2020-03-10 14:02:56 -04001678void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1679 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001680 if (sh->isOpaque()) {
1681 if (op == SkClipOp::kIntersect) {
1682 // we don't occlude anything, so skip this call
1683 } else {
1684 SkASSERT(op == SkClipOp::kDifference);
1685 // we occlude everything, so set the clip to empty
1686 this->clipRect({0,0,0,0});
1687 }
1688 } else {
1689 this->onClipShader(std::move(sh), op);
1690 }
Mike Reed121c2af2020-03-10 14:02:56 -04001691 }
1692}
1693
1694void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1695 AutoValidateClip avc(this);
1696
1697 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1698
1699 // we don't know how to mutate our conservative bounds, so we don't
1700}
1701
Mike Reedc1f77742016-12-09 09:00:50 -05001702void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001703 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001704 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001705}
1706
Mike Reedc1f77742016-12-09 09:00:50 -05001707void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001708 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001709
reed@google.com5c3d1472011-02-22 19:12:23 +00001710 AutoValidateClip avc(this);
1711
Mike Reed20800c82017-11-15 16:09:04 -05001712 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001713 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714}
1715
reed@google.com819c9212011-02-23 18:56:55 +00001716#ifdef SK_DEBUG
1717void SkCanvas::validateClip() const {
1718 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001719 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001720 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001721 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001722 return;
1723 }
reed@google.com819c9212011-02-23 18:56:55 +00001724}
1725#endif
1726
Mike Reeda1361362017-03-07 09:37:29 -05001727bool SkCanvas::androidFramework_isClipAA() const {
1728 bool containsAA = false;
1729
1730 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1731
1732 return containsAA;
1733}
1734
1735class RgnAccumulator {
1736 SkRegion* fRgn;
1737public:
1738 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1739 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1740 SkIPoint origin = device->getOrigin();
1741 if (origin.x() | origin.y()) {
1742 rgn->translate(origin.x(), origin.y());
1743 }
1744 fRgn->op(*rgn, SkRegion::kUnion_Op);
1745 }
1746};
1747
1748void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1749 RgnAccumulator accum(rgn);
1750 SkRegion tmp;
1751
1752 rgn->setEmpty();
1753 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001754}
1755
reed@google.com5c3d1472011-02-22 19:12:23 +00001756///////////////////////////////////////////////////////////////////////////////
1757
reed@google.com754de5f2014-02-24 19:38:20 +00001758bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001759 return fMCRec->fRasterClip.isEmpty();
1760
1761 // TODO: should we only use the conservative answer in a recording canvas?
1762#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001763 SkBaseDevice* dev = this->getTopDevice();
1764 // if no device we return true
1765 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001766#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001767}
1768
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001769bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001770 SkBaseDevice* dev = this->getTopDevice();
1771 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001772 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001773}
1774
msarettfbfa2582016-08-12 08:29:08 -07001775static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1776#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1777 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1778 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1779 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1780 return 0xF != _mm_movemask_ps(mask);
1781#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1782 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1783 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1784 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1785 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1786#else
1787 SkRect devRectAsRect;
1788 SkRect devClipAsRect;
1789 devRect.store(&devRectAsRect.fLeft);
1790 devClip.store(&devClipAsRect.fLeft);
1791 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1792#endif
1793}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001794
msarettfbfa2582016-08-12 08:29:08 -07001795// It's important for this function to not be inlined. Otherwise the compiler will share code
1796// between the fast path and the slow path, resulting in two slow paths.
1797static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1798 const SkMatrix& matrix) {
1799 SkRect deviceRect;
1800 matrix.mapRect(&deviceRect, src);
1801 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1802}
1803
1804bool SkCanvas::quickReject(const SkRect& src) const {
1805#ifdef SK_DEBUG
1806 // Verify that fDeviceClipBounds are set properly.
1807 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001808 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001809 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001810 } else {
msarettfbfa2582016-08-12 08:29:08 -07001811 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 }
msarettfbfa2582016-08-12 08:29:08 -07001813
msarett9637ea92016-08-18 14:03:30 -07001814 // Verify that fIsScaleTranslate is set properly.
1815 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001816#endif
1817
msarett9637ea92016-08-18 14:03:30 -07001818 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001819 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1820 }
1821
1822 // We inline the implementation of mapScaleTranslate() for the fast path.
1823 float sx = fMCRec->fMatrix.getScaleX();
1824 float sy = fMCRec->fMatrix.getScaleY();
1825 float tx = fMCRec->fMatrix.getTranslateX();
1826 float ty = fMCRec->fMatrix.getTranslateY();
1827 Sk4f scale(sx, sy, sx, sy);
1828 Sk4f trans(tx, ty, tx, ty);
1829
1830 // Apply matrix.
1831 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1832
1833 // Make sure left < right, top < bottom.
1834 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1835 Sk4f min = Sk4f::Min(ltrb, rblt);
1836 Sk4f max = Sk4f::Max(ltrb, rblt);
1837 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1838 // ARM this sequence generates the fastest (a single instruction).
1839 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1840
1841 // Check if the device rect is NaN or outside the clip.
1842 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843}
1844
reed@google.com3b3e8952012-08-16 20:53:31 +00001845bool SkCanvas::quickReject(const SkPath& path) const {
1846 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847}
1848
Mike Klein83c8dd92017-11-28 17:08:45 -05001849SkRect SkCanvas::getLocalClipBounds() const {
1850 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001851 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001852 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853 }
1854
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001855 SkMatrix inverse;
1856 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001857 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001858 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001859 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001860
Mike Reed42e8c532017-01-23 14:09:13 -05001861 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001862 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001863 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001864
Mike Reedb57b9312018-04-23 12:12:54 -04001865 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001866 inverse.mapRect(&bounds, r);
1867 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001868}
1869
Mike Klein83c8dd92017-11-28 17:08:45 -05001870SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001871 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001872}
1873
Mike Reedb18e74d2020-01-16 13:58:22 -05001874///////////////////////////////////////////////////////////////////////
1875
Mike Reed403c8072020-01-08 10:40:39 -05001876SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001877 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878}
1879
Mike Reed46f5c5f2020-02-20 15:42:29 -05001880SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001881 return fMCRec->fMatrix;
1882}
1883
Brian Osman11052242016-10-27 14:47:55 -04001884GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001885 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001886 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001887}
1888
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001889GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001890 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001891 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001892}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001893
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001894void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1895 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001896 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001897 if (outer.isEmpty()) {
1898 return;
1899 }
1900 if (inner.isEmpty()) {
1901 this->drawRRect(outer, paint);
1902 return;
1903 }
1904
1905 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001906 // be able to return ...
1907 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001908 //
1909 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001910 if (!outer.getBounds().contains(inner.getBounds())) {
1911 return;
1912 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001913
1914 this->onDrawDRRect(outer, inner, paint);
1915}
1916
reed41af9662015-01-05 07:49:08 -08001917void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001918 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001919 this->onDrawPaint(paint);
1920}
1921
1922void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001923 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001924 // To avoid redundant logic in our culling code and various backends, we always sort rects
1925 // before passing them along.
1926 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001927}
1928
Mike Reedd5674082019-04-19 15:00:47 -04001929void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1930 TRACE_EVENT0("skia", TRACE_FUNC);
1931 this->onDrawBehind(paint);
1932}
1933
msarettdca352e2016-08-26 06:37:45 -07001934void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001935 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001936 if (region.isEmpty()) {
1937 return;
1938 }
1939
1940 if (region.isRect()) {
1941 return this->drawIRect(region.getBounds(), paint);
1942 }
1943
1944 this->onDrawRegion(region, paint);
1945}
1946
reed41af9662015-01-05 07:49:08 -08001947void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001948 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001949 // To avoid redundant logic in our culling code and various backends, we always sort rects
1950 // before passing them along.
1951 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001952}
1953
1954void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001955 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001956 this->onDrawRRect(rrect, paint);
1957}
1958
1959void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001960 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001961 this->onDrawPoints(mode, count, pts, paint);
1962}
1963
Mike Reede88a1cb2017-03-17 09:50:46 -04001964void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1965 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001966 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001967}
1968
1969void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001970 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001971 RETURN_ON_NULL(vertices);
Brian Osmanf11e3312020-03-24 14:57:38 -04001972
Mike Reed5caf9352020-03-02 14:57:09 -05001973 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Brian Osman8cbedf92020-03-31 10:38:31 -04001974 SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);
Brian Osmanf11e3312020-03-24 14:57:38 -04001975
1976 // If the vertices contain custom attributes, ensure they line up with the paint's shader
1977 const SkRuntimeEffect* effect =
1978 paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001979 if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) {
Brian Osmanf11e3312020-03-24 14:57:38 -04001980 return;
1981 }
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001982 if (effect) {
1983 int attrIndex = 0;
1984 for (const auto& v : effect->varyings()) {
1985 if (vertices->priv().attributes()[attrIndex++].channelCount() != v.fWidth) {
1986 return;
1987 }
1988 }
1989 }
Brian Osmanf11e3312020-03-24 14:57:38 -04001990
Mike Reed5caf9352020-03-02 14:57:09 -05001991 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001992}
1993
1994void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001995 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001996 this->onDrawPath(path, paint);
1997}
1998
reeda85d4d02015-05-06 12:56:48 -07001999void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002000 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002001 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07002002 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08002003}
2004
Mike Reedc4e31092018-01-30 11:15:27 -05002005// Returns true if the rect can be "filled" : non-empty and finite
2006static bool fillable(const SkRect& r) {
2007 SkScalar w = r.width();
2008 SkScalar h = r.height();
2009 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
2010}
2011
reede47829b2015-08-06 10:02:53 -07002012void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2013 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002014 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002015 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002016 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002017 return;
2018 }
2019 this->onDrawImageRect(image, &src, dst, paint, constraint);
2020}
reed41af9662015-01-05 07:49:08 -08002021
reed84984ef2015-07-17 07:09:43 -07002022void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2023 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002024 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002025 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002026}
2027
Brian Salomonf08002c2018-10-26 16:15:46 -04002028void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002029 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002030 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002031 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002032}
reede47829b2015-08-06 10:02:53 -07002033
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002034namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002035class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002036public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002037 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2038 if (!origPaint) {
2039 return;
2040 }
2041 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2042 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2043 }
2044 if (origPaint->getMaskFilter()) {
2045 fPaint.writable()->setMaskFilter(nullptr);
2046 }
2047 if (origPaint->isAntiAlias()) {
2048 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002049 }
2050 }
2051
2052 const SkPaint* get() const {
2053 return fPaint;
2054 }
2055
2056private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002057 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002058};
2059} // namespace
2060
reed4c21dc52015-06-25 12:32:03 -07002061void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2062 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002063 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002064 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002065 if (dst.isEmpty()) {
2066 return;
2067 }
msarett552bca92016-08-03 06:53:26 -07002068 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002069 LatticePaint latticePaint(paint);
2070 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002071 } else {
reede47829b2015-08-06 10:02:53 -07002072 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002073 }
reed4c21dc52015-06-25 12:32:03 -07002074}
2075
msarett16882062016-08-16 09:31:08 -07002076void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2077 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002078 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002079 RETURN_ON_NULL(image);
2080 if (dst.isEmpty()) {
2081 return;
2082 }
msarett71df2d72016-09-30 12:41:42 -07002083
2084 SkIRect bounds;
2085 Lattice latticePlusBounds = lattice;
2086 if (!latticePlusBounds.fBounds) {
2087 bounds = SkIRect::MakeWH(image->width(), image->height());
2088 latticePlusBounds.fBounds = &bounds;
2089 }
2090
2091 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002092 LatticePaint latticePaint(paint);
2093 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002094 } else {
2095 this->drawImageRect(image, dst, paint);
2096 }
2097}
2098
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002099static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002100 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002101 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002102 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002103 return SkImage::MakeFromBitmap(bitmap);
2104}
2105
2106void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2107 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002108}
2109
reede47829b2015-08-06 10:02:53 -07002110void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002111 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002112 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002113}
2114
reed84984ef2015-07-17 07:09:43 -07002115void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2116 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002117 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002118}
2119
reede47829b2015-08-06 10:02:53 -07002120void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2121 SrcRectConstraint constraint) {
2122 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2123 constraint);
2124}
reede47829b2015-08-06 10:02:53 -07002125
reed71c3c762015-06-24 10:29:17 -07002126void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002127 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002128 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002129 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002130 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002131 if (count <= 0) {
2132 return;
2133 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002134 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002135 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002136 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002137}
2138
reedf70b5312016-03-04 16:36:20 -08002139void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002140 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002141 if (key) {
2142 this->onDrawAnnotation(rect, key, value);
2143 }
2144}
2145
reede47829b2015-08-06 10:02:53 -07002146void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2147 const SkPaint* paint, SrcRectConstraint constraint) {
2148 if (src) {
2149 this->drawImageRect(image, *src, dst, paint, constraint);
2150 } else {
2151 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2152 dst, paint, constraint);
2153 }
2154}
reede47829b2015-08-06 10:02:53 -07002155
Mike Reed4204da22017-05-17 08:53:36 -04002156void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002157 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002158 this->onDrawShadowRec(path, rec);
2159}
2160
2161void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2162 SkPaint paint;
2163 const SkRect& pathBounds = path.getBounds();
2164
Mike Reed38992392019-07-30 10:48:15 -04002165 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002166 while (iter.next()) {
2167 iter.fDevice->drawShadow(path, rec);
2168 }
Mike Reed38992392019-07-30 10:48:15 -04002169 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002170}
2171
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002172void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002173 QuadAAFlags aaFlags, const SkColor4f& color,
2174 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002175 TRACE_EVENT0("skia", TRACE_FUNC);
2176 // Make sure the rect is sorted before passing it along
2177 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2178}
2179
2180void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2181 const SkPoint dstClips[],
2182 const SkMatrix preViewMatrices[],
2183 const SkPaint* paint,
2184 SrcRectConstraint constraint) {
2185 TRACE_EVENT0("skia", TRACE_FUNC);
2186 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2187}
2188
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189//////////////////////////////////////////////////////////////////////////////
2190// These are the virtual drawing methods
2191//////////////////////////////////////////////////////////////////////////////
2192
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002193void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002194 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002195 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2196 }
2197}
2198
reed41af9662015-01-05 07:49:08 -08002199void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002200 this->internalDrawPaint(paint);
2201}
2202
2203void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002204 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205
2206 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002207 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 }
2209
Mike Reed38992392019-07-30 10:48:15 -04002210 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211}
2212
reed41af9662015-01-05 07:49:08 -08002213void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2214 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215 if ((long)count <= 0) {
2216 return;
2217 }
2218
Mike Reed822128b2017-02-28 16:41:03 -05002219 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002220 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002221 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002222 // special-case 2 points (common for drawing a single line)
2223 if (2 == count) {
2224 r.set(pts[0], pts[1]);
2225 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002226 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002227 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002228 if (!r.isFinite()) {
2229 return;
2230 }
Mike Reed822128b2017-02-28 16:41:03 -05002231 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002232 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2233 return;
2234 }
2235 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002236 }
reed@google.coma584aed2012-05-16 14:06:02 +00002237
halcanary96fcdcc2015-08-27 07:41:13 -07002238 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239
Mike Reed38992392019-07-30 10:48:15 -04002240 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002241
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002243 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244 }
reed@google.com4b226022011-01-11 18:32:13 +00002245
Mike Reed38992392019-07-30 10:48:15 -04002246 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247}
2248
reed4a167172016-08-18 17:15:25 -07002249static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002250 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002251}
2252
reed41af9662015-01-05 07:49:08 -08002253void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002254 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002256 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002257 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002258 return;
2259 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 }
reed@google.com4b226022011-01-11 18:32:13 +00002261
reed4a167172016-08-18 17:15:25 -07002262 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002263 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264
reed4a167172016-08-18 17:15:25 -07002265 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002266 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002267 }
2268
Mike Reed38992392019-07-30 10:48:15 -04002269 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002270 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002271 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002272 SkDrawIter iter(this);
2273 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002274 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277}
2278
msarett44df6512016-08-25 13:54:30 -07002279void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002280 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002281 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002282 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002283 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2284 return;
2285 }
msarett44df6512016-08-25 13:54:30 -07002286 }
2287
Mike Reed38992392019-07-30 10:48:15 -04002288 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002289
2290 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002291 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002292 }
2293
Mike Reed38992392019-07-30 10:48:15 -04002294 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002295}
2296
Mike Reedd5674082019-04-19 15:00:47 -04002297void SkCanvas::onDrawBehind(const SkPaint& paint) {
2298 SkIRect bounds;
2299 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2300 for (;;) {
2301 const MCRec* rec = (const MCRec*)iter.prev();
2302 if (!rec) {
2303 return; // no backimages, so nothing to draw
2304 }
2305 if (rec->fBackImage) {
2306 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2307 rec->fBackImage->fImage->width(),
2308 rec->fBackImage->fImage->height());
2309 break;
2310 }
2311 }
2312
Mike Reed38992392019-07-30 10:48:15 -04002313 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002314
2315 while (iter.next()) {
2316 SkBaseDevice* dev = iter.fDevice;
2317
Mike Reedd5674082019-04-19 15:00:47 -04002318 dev->save();
2319 // We use clipRegion because it is already defined to operate in dev-space
2320 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2321 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002322 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002323 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002324 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002325 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002326 }
2327
Mike Reed38992392019-07-30 10:48:15 -04002328 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002329}
2330
reed41af9662015-01-05 07:49:08 -08002331void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002332 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002333 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002334 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002335 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002336 return;
2337 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002338 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002339
Mike Reed38992392019-07-30 10:48:15 -04002340 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002341
2342 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002343 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002344 }
2345
Mike Reed38992392019-07-30 10:48:15 -04002346 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002347}
2348
bsalomonac3aa242016-08-19 11:25:19 -07002349void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2350 SkScalar sweepAngle, bool useCenter,
2351 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002352 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002353 if (paint.canComputeFastBounds()) {
2354 SkRect storage;
2355 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002356 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002357 return;
2358 }
bsalomonac3aa242016-08-19 11:25:19 -07002359 }
2360
Mike Reed38992392019-07-30 10:48:15 -04002361 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002362
2363 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002364 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002365 }
2366
Mike Reed38992392019-07-30 10:48:15 -04002367 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002368}
2369
reed41af9662015-01-05 07:49:08 -08002370void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002371 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002372 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002373 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2374 return;
2375 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002376 }
2377
2378 if (rrect.isRect()) {
2379 // call the non-virtual version
2380 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002381 return;
2382 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002383 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002384 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2385 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002386 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002387
Mike Reed38992392019-07-30 10:48:15 -04002388 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002389
2390 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002391 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002392 }
2393
Mike Reed38992392019-07-30 10:48:15 -04002394 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002395}
2396
Mike Reed822128b2017-02-28 16:41:03 -05002397void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002398 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002399 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002400 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2401 return;
2402 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002403 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002404
Mike Reed38992392019-07-30 10:48:15 -04002405 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002406
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002407 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002408 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002409 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002410
Mike Reed38992392019-07-30 10:48:15 -04002411 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002412}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002413
reed41af9662015-01-05 07:49:08 -08002414void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002415 if (!path.isFinite()) {
2416 return;
2417 }
2418
Mike Reed822128b2017-02-28 16:41:03 -05002419 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002420 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002421 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002422 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2423 return;
2424 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002425 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002426
Mike Reed822128b2017-02-28 16:41:03 -05002427 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002428 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002429 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002430 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002431 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002432 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433
Mike Reed38992392019-07-30 10:48:15 -04002434 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435
2436 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002437 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002438 }
2439
Mike Reed38992392019-07-30 10:48:15 -04002440 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441}
2442
reed262a71b2015-12-05 13:07:27 -08002443bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002444 if (!paint.getImageFilter()) {
2445 return false;
2446 }
2447
2448 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002449 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002450 return false;
2451 }
2452
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002453 // The other paint effects need to be applied before the image filter, but the sprite draw
2454 // applies the filter explicitly first.
2455 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2456 return false;
2457 }
reed262a71b2015-12-05 13:07:27 -08002458 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2459 // Once we can filter and the filter will return a result larger than itself, we should be
2460 // able to remove this constraint.
2461 // skbug.com/4526
2462 //
2463 SkPoint pt;
2464 ctm.mapXY(x, y, &pt);
2465 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2466 return ir.contains(fMCRec->fRasterClip.getBounds());
2467}
2468
Mike Reedf441cfc2018-04-11 14:50:16 -04002469// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2470// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2471// null.
2472static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2473 if (paintParam) {
2474 *real = *paintParam;
2475 real->setStyle(SkPaint::kFill_Style);
2476 real->setPathEffect(nullptr);
2477 paintParam = real;
2478 }
2479 return paintParam;
2480}
2481
reeda85d4d02015-05-06 12:56:48 -07002482void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002483 SkPaint realPaint;
2484 paint = init_image_paint(&realPaint, paint);
2485
reeda85d4d02015-05-06 12:56:48 -07002486 SkRect bounds = SkRect::MakeXYWH(x, y,
2487 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002488 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002489 SkRect tmp = bounds;
2490 if (paint) {
2491 paint->computeFastBounds(tmp, &tmp);
2492 }
2493 if (this->quickReject(tmp)) {
2494 return;
2495 }
reeda85d4d02015-05-06 12:56:48 -07002496 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002497 // At this point we need a real paint object. If the caller passed null, then we should
2498 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2499 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2500 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002501
reeda2217ef2016-07-20 06:04:34 -07002502 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002503 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2504 *paint);
2505 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002506 special = this->getDevice()->makeSpecial(image);
2507 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002508 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002509 }
2510 }
2511
Mike Reed38992392019-07-30 10:48:15 -04002512 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002513
reeda85d4d02015-05-06 12:56:48 -07002514 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002515 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002516 if (special) {
2517 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002518 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002519 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002520 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002521 SkScalarRoundToInt(pt.fY), pnt,
2522 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002523 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002524 iter.fDevice->drawImageRect(
2525 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2526 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002527 }
reeda85d4d02015-05-06 12:56:48 -07002528 }
halcanary9d524f22016-03-29 09:03:52 -07002529
Mike Reed38992392019-07-30 10:48:15 -04002530 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002531}
2532
reed41af9662015-01-05 07:49:08 -08002533void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002534 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002535 SkPaint realPaint;
2536 paint = init_image_paint(&realPaint, paint);
2537
halcanary96fcdcc2015-08-27 07:41:13 -07002538 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002539 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002540 if (paint) {
2541 paint->computeFastBounds(dst, &storage);
2542 }
2543 if (this->quickReject(storage)) {
2544 return;
2545 }
reeda85d4d02015-05-06 12:56:48 -07002546 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002547 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002548
Mike Reed38992392019-07-30 10:48:15 -04002549 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002550
reeda85d4d02015-05-06 12:56:48 -07002551 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002552 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002553 }
halcanary9d524f22016-03-29 09:03:52 -07002554
Mike Reed38992392019-07-30 10:48:15 -04002555 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002556}
2557
reed4c21dc52015-06-25 12:32:03 -07002558void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2559 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002560 SkPaint realPaint;
2561 paint = init_image_paint(&realPaint, paint);
2562
halcanary96fcdcc2015-08-27 07:41:13 -07002563 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002564 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002565 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2566 return;
2567 }
reed@google.com3d608122011-11-21 15:16:16 +00002568 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002569 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002570
Mike Reed38992392019-07-30 10:48:15 -04002571 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002572
reed4c21dc52015-06-25 12:32:03 -07002573 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002574 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002575 }
halcanary9d524f22016-03-29 09:03:52 -07002576
Mike Reed38992392019-07-30 10:48:15 -04002577 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002578}
2579
msarett16882062016-08-16 09:31:08 -07002580void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2581 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002582 SkPaint realPaint;
2583 paint = init_image_paint(&realPaint, paint);
2584
msarett16882062016-08-16 09:31:08 -07002585 if (nullptr == paint || paint->canComputeFastBounds()) {
2586 SkRect storage;
2587 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2588 return;
2589 }
2590 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002591 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002592
Mike Reed38992392019-07-30 10:48:15 -04002593 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002594
2595 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002596 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002597 }
2598
Mike Reed38992392019-07-30 10:48:15 -04002599 DRAW_END
msarett16882062016-08-16 09:31:08 -07002600}
2601
fmalita00d5c2c2014-08-21 08:53:26 -07002602void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2603 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002604 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002605 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002606 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002607 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002608 SkRect tmp;
2609 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2610 return;
2611 }
2612 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002613 }
2614
fmalita024f9962015-03-03 19:08:17 -08002615 // We cannot filter in the looper as we normally do, because the paint is
2616 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002617 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002618
fmalitaaa1b9122014-08-28 14:32:24 -07002619 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002620 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002621 }
2622
Mike Reed38992392019-07-30 10:48:15 -04002623 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002624}
2625
Mike Reed358fcad2018-11-23 15:27:51 -05002626// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002627void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002628 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2629 TRACE_EVENT0("skia", TRACE_FUNC);
2630 if (byteLength) {
2631 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002632 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002633 }
2634}
Mike Reed4f81bb72019-01-23 09:23:00 -05002635
fmalita00d5c2c2014-08-21 08:53:26 -07002636void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2637 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002638 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002639 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002640 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002641 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002642}
reed@google.come0d9ce82014-04-23 04:00:17 +00002643
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002644void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2645 const SkPaint& paint) {
2646 DRAW_BEGIN(paint, nullptr)
2647
2648 while (iter.next()) {
2649 // In the common case of one iteration we could std::move vertices here.
2650 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2651 }
2652
2653 DRAW_END
2654}
Brian Salomon199fb872017-02-06 09:41:10 -05002655
dandovb3c9d1c2014-08-12 08:34:29 -07002656void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002657 const SkPoint texCoords[4], SkBlendMode bmode,
2658 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002659 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002660 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002661 return;
2662 }
mtklein6cfa73a2014-08-13 13:33:49 -07002663
Mike Reedfaba3712016-11-03 14:45:31 -04002664 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002665}
2666
2667void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002668 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002669 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002670 // Since a patch is always within the convex hull of the control points, we discard it when its
2671 // bounding rectangle is completely outside the current clip.
2672 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002673 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002674 if (this->quickReject(bounds)) {
2675 return;
2676 }
mtklein6cfa73a2014-08-13 13:33:49 -07002677
Mike Reed38992392019-07-30 10:48:15 -04002678 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002679
dandovecfff212014-08-04 10:02:00 -07002680 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002681 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002682 }
mtklein6cfa73a2014-08-13 13:33:49 -07002683
Mike Reed38992392019-07-30 10:48:15 -04002684 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002685}
2686
reeda8db7282015-07-07 10:22:31 -07002687void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002688#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002689 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002690#endif
reede3b38ce2016-01-08 09:18:44 -08002691 RETURN_ON_NULL(dr);
2692 if (x || y) {
2693 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2694 this->onDrawDrawable(dr, &matrix);
2695 } else {
2696 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002697 }
2698}
2699
reeda8db7282015-07-07 10:22:31 -07002700void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002701#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002702 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002703#endif
reede3b38ce2016-01-08 09:18:44 -08002704 RETURN_ON_NULL(dr);
2705 if (matrix && matrix->isIdentity()) {
2706 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002707 }
reede3b38ce2016-01-08 09:18:44 -08002708 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002709}
2710
2711void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002712 // drawable bounds are no longer reliable (e.g. android displaylist)
2713 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002714 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002715}
2716
reed71c3c762015-06-24 10:29:17 -07002717void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002718 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002719 const SkRect* cull, const SkPaint* paint) {
2720 if (cull && this->quickReject(*cull)) {
2721 return;
2722 }
2723
2724 SkPaint pnt;
2725 if (paint) {
2726 pnt = *paint;
2727 }
halcanary9d524f22016-03-29 09:03:52 -07002728
Mike Reed38992392019-07-30 10:48:15 -04002729 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002730 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002731 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002732 }
Mike Reed38992392019-07-30 10:48:15 -04002733 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002734}
2735
reedf70b5312016-03-04 16:36:20 -08002736void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2737 SkASSERT(key);
2738
2739 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002740 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002741 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002742 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002743 }
Mike Reed38992392019-07-30 10:48:15 -04002744 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002745}
2746
Michael Ludwiga595f862019-08-27 15:25:49 -04002747void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2748 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002749 SkASSERT(r.isSorted());
2750
2751 // If this used a paint, it would be a filled color with blend mode, which does not
2752 // need to use an autodraw loop, so use SkDrawIter directly.
2753 if (this->quickReject(r)) {
2754 return;
2755 }
2756
Michael Ludwiga4b44882019-08-28 14:34:58 -04002757 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002758 SkDrawIter iter(this);
2759 while(iter.next()) {
2760 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2761 }
2762}
2763
2764void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2765 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2766 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002767 if (count <= 0) {
2768 // Nothing to draw
2769 return;
2770 }
2771
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002772 SkPaint realPaint;
2773 init_image_paint(&realPaint, paint);
2774
Michael Ludwiga4b44882019-08-28 14:34:58 -04002775 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2776 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2777 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2778 // or we need it for the autolooper (since it greatly improves image filter perf).
2779 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2780 bool setBoundsValid = count == 1 || needsAutoLooper;
2781 SkRect setBounds = imageSet[0].fDstRect;
2782 if (imageSet[0].fMatrixIndex >= 0) {
2783 // Account for the per-entry transform that is applied prior to the CTM when drawing
2784 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002785 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002786 if (needsAutoLooper) {
2787 for (int i = 1; i < count; ++i) {
2788 SkRect entryBounds = imageSet[i].fDstRect;
2789 if (imageSet[i].fMatrixIndex >= 0) {
2790 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2791 }
2792 setBounds.joinPossiblyEmptyRect(entryBounds);
2793 }
2794 }
2795
2796 // If we happen to have the draw bounds, though, might as well check quickReject().
2797 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2798 SkRect tmp;
2799 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2800 return;
2801 }
2802 }
2803
2804 if (needsAutoLooper) {
2805 SkASSERT(setBoundsValid);
2806 DRAW_BEGIN(realPaint, &setBounds)
2807 while (iter.next()) {
2808 iter.fDevice->drawEdgeAAImageSet(
2809 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2810 }
2811 DRAW_END
2812 } else {
2813 this->predrawNotify();
2814 SkDrawIter iter(this);
2815 while(iter.next()) {
2816 iter.fDevice->drawEdgeAAImageSet(
2817 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2818 }
2819 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002820}
2821
reed@android.com8a1c16f2008-12-17 15:59:43 +00002822//////////////////////////////////////////////////////////////////////////////
2823// These methods are NOT virtual, and therefore must call back into virtual
2824// methods, rather than actually drawing themselves.
2825//////////////////////////////////////////////////////////////////////////////
2826
reed374772b2016-10-05 17:33:02 -07002827void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002828 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002829 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002830 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831 this->drawPaint(paint);
2832}
2833
2834void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002835 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002836 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2837}
2838
Mike Reed3661bc92017-02-22 13:21:42 -05002839void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002840 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002841 pts[0].set(x0, y0);
2842 pts[1].set(x1, y1);
2843 this->drawPoints(kLines_PointMode, 2, pts, paint);
2844}
2845
Mike Reed3661bc92017-02-22 13:21:42 -05002846void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002847 if (radius < 0) {
2848 radius = 0;
2849 }
2850
2851 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002852 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002853 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002854}
2855
2856void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2857 const SkPaint& paint) {
2858 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002859 SkRRect rrect;
2860 rrect.setRectXY(r, rx, ry);
2861 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 } else {
2863 this->drawRect(r, paint);
2864 }
2865}
2866
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2868 SkScalar sweepAngle, bool useCenter,
2869 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002870 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002871 if (oval.isEmpty() || !sweepAngle) {
2872 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002873 }
bsalomon21af9ca2016-08-25 12:29:23 -07002874 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002875}
2876
reed@android.comf76bacf2009-05-13 14:00:33 +00002877///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002878#ifdef SK_DISABLE_SKPICTURE
2879void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002880
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002881
2882void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2883 const SkPaint* paint) {}
2884#else
Mike Klein88d90712018-01-27 17:30:04 +00002885/**
2886 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2887 * against the playback cost of recursing into the subpicture to get at its actual ops.
2888 *
2889 * For now we pick a conservatively small value, though measurement (and other heuristics like
2890 * the type of ops contained) may justify changing this value.
2891 */
2892#define kMaxPictureOpsToUnrollInsteadOfRef 1
2893
reedd5fa1a42014-08-09 11:08:05 -07002894void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002895 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002896 RETURN_ON_NULL(picture);
2897
reede3b38ce2016-01-08 09:18:44 -08002898 if (matrix && matrix->isIdentity()) {
2899 matrix = nullptr;
2900 }
Mike Klein88d90712018-01-27 17:30:04 +00002901 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2902 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2903 picture->playback(this);
2904 } else {
2905 this->onDrawPicture(picture, matrix, paint);
2906 }
reedd5fa1a42014-08-09 11:08:05 -07002907}
robertphillips9b14f262014-06-04 05:40:44 -07002908
reedd5fa1a42014-08-09 11:08:05 -07002909void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2910 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002911 if (!paint || paint->canComputeFastBounds()) {
2912 SkRect bounds = picture->cullRect();
2913 if (paint) {
2914 paint->computeFastBounds(bounds, &bounds);
2915 }
2916 if (matrix) {
2917 matrix->mapRect(&bounds);
2918 }
2919 if (this->quickReject(bounds)) {
2920 return;
2921 }
2922 }
2923
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002924 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002925 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002926}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002927#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002928
reed@android.com8a1c16f2008-12-17 15:59:43 +00002929///////////////////////////////////////////////////////////////////////////////
2930///////////////////////////////////////////////////////////////////////////////
2931
reed3aafe112016-08-18 12:45:34 -07002932SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002933 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934
2935 SkASSERT(canvas);
2936
reed3aafe112016-08-18 12:45:34 -07002937 fImpl = new (fStorage) SkDrawIter(canvas);
Michael Ludwig2a8a3ff2020-03-26 15:33:20 -04002938 // This advances the base iterator to the first device and caches its origin,
2939 // correctly handling the case where there are no devices.
2940 this->next();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002941}
2942
2943SkCanvas::LayerIter::~LayerIter() {
2944 fImpl->~SkDrawIter();
2945}
2946
2947void SkCanvas::LayerIter::next() {
2948 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002949 if (!fDone) {
2950 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2951 // filters, so its devices will always be able to report the origin exactly.
2952 fDeviceOrigin = fImpl->fDevice->getOrigin();
2953 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002954}
2955
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002956SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002957 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958}
2959
2960const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002961 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962}
2963
2964const SkPaint& SkCanvas::LayerIter::paint() const {
2965 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002966 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002967 paint = &fDefaultPaint;
2968 }
2969 return *paint;
2970}
2971
Mike Reedca37f322018-03-08 13:22:16 -05002972SkIRect SkCanvas::LayerIter::clipBounds() const {
2973 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002974}
2975
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002976int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2977int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002978
2979///////////////////////////////////////////////////////////////////////////////
2980
Brian Osmane8a98632019-04-10 10:26:10 -04002981SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2982SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2983SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2984SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
2985
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002986SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2987 const SkRect& dstRect, int matrixIndex, float alpha,
2988 unsigned aaFlags, bool hasClip)
2989 : fImage(std::move(image))
2990 , fSrcRect(srcRect)
2991 , fDstRect(dstRect)
2992 , fMatrixIndex(matrixIndex)
2993 , fAlpha(alpha)
2994 , fAAFlags(aaFlags)
2995 , fHasClip(hasClip) {}
2996
2997SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2998 const SkRect& dstRect, float alpha, unsigned aaFlags)
2999 : fImage(std::move(image))
3000 , fSrcRect(srcRect)
3001 , fDstRect(dstRect)
3002 , fAlpha(alpha)
3003 , fAAFlags(aaFlags) {}
3004
3005///////////////////////////////////////////////////////////////////////////////
3006
Mike Reed5df49342016-11-12 08:06:55 -06003007std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003008 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003009 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003010 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003011 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003012
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003013 SkBitmap bitmap;
3014 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003015 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003016 }
Mike Reed12f77342017-11-08 11:19:52 -05003017
3018 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003019 std::make_unique<SkCanvas>(bitmap, *props) :
3020 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003021}
reedd5fa1a42014-08-09 11:08:05 -07003022
3023///////////////////////////////////////////////////////////////////////////////
3024
Florin Malitaee424ac2016-12-01 12:47:59 -05003025SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003026 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003027
Florin Malita439ace92016-12-02 12:05:41 -05003028SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003029 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003030
Herb Derbyefe39bc2018-05-01 17:06:20 -04003031SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003032 : INHERITED(device) {}
3033
Florin Malitaee424ac2016-12-01 12:47:59 -05003034SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3035 (void)this->INHERITED::getSaveLayerStrategy(rec);
3036 return kNoLayer_SaveLayerStrategy;
3037}
3038
Mike Reed148b7fd2018-12-18 17:38:18 -05003039bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3040 return false;
3041}
3042
Florin Malitaee424ac2016-12-01 12:47:59 -05003043///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003044
reed73603f32016-09-20 08:42:38 -07003045static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3046static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3047static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3048static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3049static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3050static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003051
3052///////////////////////////////////////////////////////////////////////////////////////////////////
3053
3054SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3055 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003056 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003057 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003058 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003059 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003060 clip.setEmpty();
3061 }
3062
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003063 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003064 return handle;
3065 }
3066 return nullptr;
3067}
3068
3069static bool install(SkBitmap* bm, const SkImageInfo& info,
3070 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003071 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003072}
3073
3074SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3075 SkBitmap* bm) {
3076 SkRasterHandleAllocator::Rec rec;
3077 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3078 return nullptr;
3079 }
3080 return rec.fHandle;
3081}
3082
3083std::unique_ptr<SkCanvas>
3084SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3085 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003086 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003087 return nullptr;
3088 }
3089
3090 SkBitmap bm;
3091 Handle hndl;
3092
3093 if (rec) {
3094 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3095 } else {
3096 hndl = alloc->allocBitmap(info, &bm);
3097 }
3098 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3099}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003100
3101///////////////////////////////////////////////////////////////////////////////////////////////////