blob: a263a4ade8c8c9452139d2139125e0d400022f5e [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 Reedeb1d5a22020-04-14 09:16:40 -040036#include "src/core/SkMarkerStack.h"
Mike Reed07d32b42020-01-23 11:06:20 -050037#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050038#include "src/core/SkMatrixUtils.h"
39#include "src/core/SkPaintPriv.h"
40#include "src/core/SkRasterClip.h"
41#include "src/core/SkSpecialImage.h"
42#include "src/core/SkStrikeCache.h"
43#include "src/core/SkTLazy.h"
44#include "src/core/SkTextFormatParams.h"
45#include "src/core/SkTraceEvent.h"
Mike Reedba962562020-03-12 20:33:21 -040046#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050047#include "src/image/SkImage_Base.h"
48#include "src/image/SkSurface_Base.h"
49#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040050
bungemand3ebb482015-08-05 13:57:49 -070051#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000052
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000053#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050054#include "include/gpu/GrContext.h"
55#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000056#endif
57
reede3b38ce2016-01-08 09:18:44 -080058#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050059#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080060
Mike Klein1bb7e232019-12-10 08:58:52 -060061// This is a test: static_assert with no message is a c++17 feature,
62// and std::max() is constexpr only since the c++14 stdlib.
63static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000064
Mike Reed139e5e02017-03-08 11:29:33 -050065///////////////////////////////////////////////////////////////////////////////////////////////////
66
reedc83a2972015-07-16 07:40:45 -070067/*
68 * Return true if the drawing this rect would hit every pixels in the canvas.
69 *
70 * Returns false if
71 * - rect does not contain the canvas' bounds
72 * - paint is not fill
73 * - paint would blur or otherwise change the coverage of the rect
74 */
75bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
76 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070077 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
78 (int)kNone_ShaderOverrideOpacity,
79 "need_matching_enums0");
80 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
81 (int)kOpaque_ShaderOverrideOpacity,
82 "need_matching_enums1");
83 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
84 (int)kNotOpaque_ShaderOverrideOpacity,
85 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070086
87 const SkISize size = this->getBaseLayerSize();
88 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050089
90 // if we're clipped at all, we can't overwrite the entire surface
91 {
92 SkBaseDevice* base = this->getDevice();
93 SkBaseDevice* top = this->getTopDevice();
94 if (base != top) {
95 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
96 }
97 if (!base->clipIsWideOpen()) {
98 return false;
99 }
reedc83a2972015-07-16 07:40:45 -0700100 }
101
102 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700103 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700104 return false; // conservative
105 }
halcanaryc5769b22016-08-10 07:13:21 -0700106
107 SkRect devRect;
108 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
109 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700110 return false;
111 }
112 }
113
114 if (paint) {
115 SkPaint::Style paintStyle = paint->getStyle();
116 if (!(paintStyle == SkPaint::kFill_Style ||
117 paintStyle == SkPaint::kStrokeAndFill_Style)) {
118 return false;
119 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400120 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700121 return false; // conservative
122 }
123 }
124 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
125}
126
127///////////////////////////////////////////////////////////////////////////////////////////////////
128
reed@google.comda17f752012-08-16 18:27:05 +0000129// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130//#define SK_TRACE_SAVERESTORE
131
132#ifdef SK_TRACE_SAVERESTORE
133 static int gLayerCounter;
134 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
135 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
136
137 static int gRecCounter;
138 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
139 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
140
141 static int gCanvasCounter;
142 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
143 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
144#else
145 #define inc_layer()
146 #define dec_layer()
147 #define inc_rec()
148 #define dec_rec()
149 #define inc_canvas()
150 #define dec_canvas()
151#endif
152
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000153typedef SkTLazy<SkPaint> SkLazyPaint;
154
reedc83a2972015-07-16 07:40:45 -0700155void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000156 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700157 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
158 ? SkSurface::kDiscard_ContentChangeMode
159 : SkSurface::kRetain_ContentChangeMode);
160 }
161}
162
163void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
164 ShaderOverrideOpacity overrideOpacity) {
165 if (fSurfaceBase) {
166 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
167 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
168 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
169 // and therefore we don't care which mode we're in.
170 //
171 if (fSurfaceBase->outstandingImageSnapshot()) {
172 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
173 mode = SkSurface::kDiscard_ContentChangeMode;
174 }
175 }
176 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000177 }
178}
179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000182/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 The clip/matrix/proc are fields that reflect the top of the save/restore
184 stack. Whenever the canvas changes, it marks a dirty flag, and then before
185 these are used (assuming we're not on a layer) we rebuild these cache
186 values: they reflect the top of the save stack, but translated and clipped
187 by the device's XY offset and bitmap-bounds.
188*/
189struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400190 DeviceCM* fNext;
191 sk_sp<SkBaseDevice> fDevice;
192 SkRasterClip fClip;
193 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
194 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400195 sk_sp<SkImage> fClipImage;
196 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197
Florin Malita53f77bd2017-04-28 13:48:37 -0400198 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000199 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700200 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400201 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500202 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700203 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000204 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400205 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400206 {}
reed@google.com4b226022011-01-11 18:32:13 +0000207
mtkleinfeaadee2015-04-08 11:25:48 -0700208 void reset(const SkIRect& bounds) {
209 SkASSERT(!fPaint);
210 SkASSERT(!fNext);
211 SkASSERT(fDevice);
212 fClip.setRect(bounds);
213 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214};
215
Mike Reed148b7fd2018-12-18 17:38:18 -0500216namespace {
217// Encapsulate state needed to restore from saveBehind()
218struct BackImage {
219 sk_sp<SkSpecialImage> fImage;
220 SkIPoint fLoc;
221};
222}
223
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224/* This is the record we keep for each save/restore level in the stack.
225 Since a level optionally copies the matrix and/or stack, we have pointers
226 for these fields. If the value is copied for this level, the copy is
227 stored in the ...Storage field, and the pointer points to that. If the
228 value is not copied for this level, we ignore ...Storage, and just point
229 at the corresponding value in the previous level in the stack.
230*/
231class SkCanvas::MCRec {
232public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500233 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 /* If there are any layers in the stack, this points to the top-most
235 one that is at or below this level in the stack (so we know what
236 bitmap/device to draw into from this level. This value is NOT
237 reference counted, since the real owner is either our fLayer field,
238 or a previous one in a lower level.)
239 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500240 DeviceCM* fTopLayer;
241 std::unique_ptr<BackImage> fBackImage;
242 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500243 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500244 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245
Mike Reeda1361362017-03-07 09:37:29 -0500246 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700247 fLayer = nullptr;
248 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800249 fMatrix.reset();
250 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700251
reedd9544982014-09-09 18:46:22 -0700252 // don't bother initializing fNext
253 inc_rec();
254 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400255 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700256 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700257 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800258 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700264 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 dec_rec();
266 }
mtkleinfeaadee2015-04-08 11:25:48 -0700267
268 void reset(const SkIRect& bounds) {
269 SkASSERT(fLayer);
270 SkASSERT(fDeferredSaveCount == 0);
271
272 fMatrix.reset();
273 fRasterClip.setRect(bounds);
274 fLayer->reset(bounds);
275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276};
277
Mike Reeda1361362017-03-07 09:37:29 -0500278class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279public:
Mike Reeda1361362017-03-07 09:37:29 -0500280 SkDrawIter(SkCanvas* canvas)
281 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
282 {}
reed@google.com4b226022011-01-11 18:32:13 +0000283
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000285 const DeviceCM* rec = fCurrLayer;
286 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400287 fDevice = rec->fDevice.get();
288 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700290 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 return true;
292 }
293 return false;
294 }
reed@google.com4b226022011-01-11 18:32:13 +0000295
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000297
Mike Reed99330ba2017-02-22 11:01:08 -0500298 SkBaseDevice* fDevice;
299
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 const DeviceCM* fCurrLayer;
302 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303};
304
Florin Malita713b8ef2017-04-28 10:57:24 -0400305#define FOR_EACH_TOP_DEVICE( code ) \
306 do { \
307 DeviceCM* layer = fMCRec->fTopLayer; \
308 while (layer) { \
309 SkBaseDevice* device = layer->fDevice.get(); \
310 if (device) { \
311 code; \
312 } \
313 layer = layer->fNext; \
314 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500315 } while (0)
316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317/////////////////////////////////////////////////////////////////////////////
318
reeddbc3cef2015-04-29 12:18:57 -0700319/**
320 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700321 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700322 */
reedd053ce92016-03-22 10:17:23 -0700323static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700324 SkImageFilter* imgf = paint.getImageFilter();
325 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700326 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700327 }
328
reedd053ce92016-03-22 10:17:23 -0700329 SkColorFilter* imgCFPtr;
330 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700331 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700332 }
reedd053ce92016-03-22 10:17:23 -0700333 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700334
335 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700336 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700337 // there is no existing paint colorfilter, so we can just return the imagefilter's
338 return imgCF;
339 }
340
341 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
342 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500343 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700344}
345
senorblanco87e066e2015-10-28 11:23:36 -0700346/**
347 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
348 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
349 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
350 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
351 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
352 * conservative "effective" bounds based on the settings in the paint... with one exception. This
353 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
354 * deliberately ignored.
355 */
356static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
357 const SkRect& rawBounds,
358 SkRect* storage) {
359 SkPaint tmpUnfiltered(paint);
360 tmpUnfiltered.setImageFilter(nullptr);
361 if (tmpUnfiltered.canComputeFastBounds()) {
362 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
363 } else {
364 return rawBounds;
365 }
366}
367
Mike Reed38992392019-07-30 10:48:15 -0400368class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369public:
senorblanco87e066e2015-10-28 11:23:36 -0700370 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
371 // paint. It's used to determine the size of the offscreen layer for filters.
372 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400373 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
374 bool skipLayerForImageFilter = false,
375 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400377 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000378 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700379 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380
Mike Reed38992392019-07-30 10:48:15 -0400381 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
382 SkASSERT(!fLazyPaint.isValid());
383 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700384 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700385 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700386 fPaint = paint;
387 }
388
389 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700390 /**
391 * We implement ImageFilters for a given draw by creating a layer, then applying the
392 * imagefilter to the pixels of that layer (its backing surface/image), and then
393 * we call restore() to xfer that layer to the main canvas.
394 *
395 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
396 * 2. Generate the src pixels:
397 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
398 * return (fPaint). We then draw the primitive (using srcover) into a cleared
399 * buffer/surface.
400 * 3. Restore the layer created in #1
401 * The imagefilter is passed the buffer/surface from the layer (now filled with the
402 * src pixels of the primitive). It returns a new "filtered" buffer, which we
403 * draw onto the previous layer using the xfermode from the original paint.
404 */
Mike Reed38992392019-07-30 10:48:15 -0400405
406 SkPaint restorePaint;
407 restorePaint.setImageFilter(fPaint->refImageFilter());
408 restorePaint.setBlendMode(fPaint->getBlendMode());
409
senorblanco87e066e2015-10-28 11:23:36 -0700410 SkRect storage;
411 if (rawBounds) {
412 // Make rawBounds include all paint outsets except for those due to image filters.
413 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
414 }
Mike Reed38992392019-07-30 10:48:15 -0400415 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700416 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700417 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000418
Mike Reed38992392019-07-30 10:48:15 -0400419 // Remove the restorePaint fields from our "working" paint
420 SkASSERT(!fLazyPaint.isValid());
421 SkPaint* paint = fLazyPaint.set(origPaint);
422 paint->setImageFilter(nullptr);
423 paint->setBlendMode(SkBlendMode::kSrcOver);
424 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000425 }
426 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000427
Mike Reed38992392019-07-30 10:48:15 -0400428 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700429 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000430 fCanvas->internalRestore();
431 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000432 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000434
reed@google.com4e2b3d32011-04-07 14:18:59 +0000435 const SkPaint& paint() const {
436 SkASSERT(fPaint);
437 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440private:
Mike Reed38992392019-07-30 10:48:15 -0400441 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000442 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400443 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000444 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700445 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446};
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448////////// macros to place around the internal draw calls //////////////////
449
Mike Reed38992392019-07-30 10:48:15 -0400450#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700451 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400452 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
453 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800454
455
Mike Reed38992392019-07-30 10:48:15 -0400456#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000457 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400458 AutoLayerForImageFilter draw(this, paint, true); \
459 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000460
Mike Reed38992392019-07-30 10:48:15 -0400461#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000462 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400463 AutoLayerForImageFilter draw(this, paint, false, bounds); \
464 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000465
Mike Reed38992392019-07-30 10:48:15 -0400466#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700467 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400468 AutoLayerForImageFilter draw(this, paint, false, bounds); \
469 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700470
Mike Reed38992392019-07-30 10:48:15 -0400471#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472
473////////////////////////////////////////////////////////////////////////////
474
msarettfbfa2582016-08-12 08:29:08 -0700475static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
476 if (bounds.isEmpty()) {
477 return SkRect::MakeEmpty();
478 }
479
480 // Expand bounds out by 1 in case we are anti-aliasing. We store the
481 // bounds as floats to enable a faster quick reject implementation.
482 SkRect dst;
483 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
484 return dst;
485}
486
mtkleinfeaadee2015-04-08 11:25:48 -0700487void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
488 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700489 fMCRec->reset(bounds);
490
491 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500492 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400493 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700494 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700495 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700496}
497
Hal Canary363a3f82018-10-04 11:04:48 -0400498void SkCanvas::init(sk_sp<SkBaseDevice> device) {
Mike Reedeb1d5a22020-04-14 09:16:40 -0400499 fMarkerStack = sk_make_sp<SkMarkerStack>();
500
reed2ff1fce2014-12-11 07:07:37 -0800501 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
503 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500504 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500505 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700506 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507
reeda499f902015-05-01 09:34:31 -0700508 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
509 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400510 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513
halcanary96fcdcc2015-08-27 07:41:13 -0700514 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000515
reedf92c8662014-08-18 08:02:43 -0700516 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700517 // The root device and the canvas should always have the same pixel geometry
518 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800519 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700520 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500521
Mike Reedc42a1cd2017-02-14 14:25:14 -0500522 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
Mike Reedeb1d5a22020-04-14 09:16:40 -0400523
524 device->setMarkerStack(fMarkerStack.get());
reedf92c8662014-08-18 08:02:43 -0700525 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400526
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500527 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528}
529
reed@google.comcde92112011-07-06 20:00:52 +0000530SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000531 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700532 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000534 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000535
Hal Canary363a3f82018-10-04 11:04:48 -0400536 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000537}
538
reed96a857e2015-01-25 10:33:58 -0800539SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000540 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800541 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000542{
543 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400544 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500545 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700546}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000547
Hal Canary363a3f82018-10-04 11:04:48 -0400548SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700549 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700550 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700551{
552 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700553
Mike Reed566e53c2017-03-10 10:49:45 -0500554 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400555 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700556}
557
Herb Derbyefe39bc2018-05-01 17:06:20 -0400558SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000559 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700560 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000561{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700563
Hal Canary363a3f82018-10-04 11:04:48 -0400564 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700565}
566
reed4a8126e2014-09-22 07:29:03 -0700567SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700568 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700569 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700570{
571 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700572
Mike Reed910ca0f2018-04-25 13:04:05 -0400573 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400574 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700575}
reed29c857d2014-09-21 10:25:07 -0700576
Mike Reed356f7c22017-01-10 11:58:39 -0500577SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
578 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700579 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
580 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500581 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700582{
583 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700584
Mike Reed910ca0f2018-04-25 13:04:05 -0400585 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400586 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587}
588
Mike Reed356f7c22017-01-10 11:58:39 -0500589SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
590
Matt Sarett31f99ce2017-04-11 08:46:01 -0400591#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
592SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
593 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
594 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
595 , fAllocator(nullptr)
596{
597 inc_canvas();
598
599 SkBitmap tmp(bitmap);
600 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400601 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400602 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400603}
604#endif
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606SkCanvas::~SkCanvas() {
607 // free up the contents of our deque
608 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 this->internalRestore(); // restore the last, since we're going away
611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 dec_canvas();
613}
614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615///////////////////////////////////////////////////////////////////////////////
616
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000617void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700618 this->onFlush();
619}
620
621void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000622 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000623 if (device) {
624 device->flush();
625 }
626}
627
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500628SkSurface* SkCanvas::getSurface() const {
629 return fSurfaceBase;
630}
631
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000632SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000633 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000634 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
635}
636
senorblancoafc7cce2016-02-02 18:44:15 -0800637SkIRect SkCanvas::getTopLayerBounds() const {
638 SkBaseDevice* d = this->getTopDevice();
639 if (!d) {
640 return SkIRect::MakeEmpty();
641 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500642 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800643}
644
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000645SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000647 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400649 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650}
651
Florin Malita0ed3b642017-01-13 16:56:38 +0000652SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400653 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000654}
655
Mike Reed353196f2017-07-21 11:01:18 -0400656bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000657 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400658 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000659}
660
Mike Reed353196f2017-07-21 11:01:18 -0400661bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
662 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400663}
664
665bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
666 SkPixmap pm;
667 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
668}
669
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000670bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400671 SkPixmap pm;
672 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700673 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 }
675 return false;
676}
677
Matt Sarett03dd6d52017-01-23 12:15:09 -0500678bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000679 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000680 SkBaseDevice* device = this->getDevice();
681 if (!device) {
682 return false;
683 }
684
Matt Sarett03dd6d52017-01-23 12:15:09 -0500685 // This check gives us an early out and prevents generation ID churn on the surface.
686 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
687 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400688 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500689 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000690 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000691
Matt Sarett03dd6d52017-01-23 12:15:09 -0500692 // Tell our owning surface to bump its generation ID.
693 const bool completeOverwrite =
694 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700695 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700696
Matt Sarett03dd6d52017-01-23 12:15:09 -0500697 // This can still fail, most notably in the case of a invalid color type or alpha type
698 // conversion. We could pull those checks into this function and avoid the unnecessary
699 // generation ID bump. But then we would be performing those checks twice, since they
700 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400701 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000702}
reed@google.com51df9e32010-12-23 19:29:18 +0000703
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704//////////////////////////////////////////////////////////////////////////////
705
reed2ff1fce2014-12-11 07:07:37 -0800706void SkCanvas::checkForDeferredSave() {
707 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800708 this->doSave();
709 }
710}
711
reedf0090cb2014-11-26 08:55:51 -0800712int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800713#ifdef SK_DEBUG
714 int count = 0;
715 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
716 for (;;) {
717 const MCRec* rec = (const MCRec*)iter.next();
718 if (!rec) {
719 break;
720 }
721 count += 1 + rec->fDeferredSaveCount;
722 }
723 SkASSERT(count == fSaveCount);
724#endif
725 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800726}
727
728int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800729 fSaveCount += 1;
730 fMCRec->fDeferredSaveCount += 1;
731 return this->getSaveCount() - 1; // return our prev value
732}
733
734void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800735 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700736
737 SkASSERT(fMCRec->fDeferredSaveCount > 0);
738 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800739 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800740}
741
742void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800743 if (fMCRec->fDeferredSaveCount > 0) {
744 SkASSERT(fSaveCount > 1);
745 fSaveCount -= 1;
746 fMCRec->fDeferredSaveCount -= 1;
747 } else {
748 // check for underflow
749 if (fMCStack.count() > 1) {
750 this->willRestore();
751 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700752 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800753 this->internalRestore();
754 this->didRestore();
755 }
reedf0090cb2014-11-26 08:55:51 -0800756 }
757}
758
759void SkCanvas::restoreToCount(int count) {
760 // sanity check
761 if (count < 1) {
762 count = 1;
763 }
mtkleinf0f14112014-12-12 08:46:25 -0800764
reedf0090cb2014-11-26 08:55:51 -0800765 int n = this->getSaveCount() - count;
766 for (int i = 0; i < n; ++i) {
767 this->restore();
768 }
769}
770
reed2ff1fce2014-12-11 07:07:37 -0800771void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700773 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000775
Mike Reedc42a1cd2017-02-14 14:25:14 -0500776 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777}
778
reed4960eee2015-12-18 07:09:18 -0800779bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400780 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781}
782
reed4960eee2015-12-18 07:09:18 -0800783bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700784 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400785 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
786 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
787 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
788 // filter does not require a decomposed CTM matrix, the filter space and device space are the
789 // same. When it has been decomposed, we want the original image filter node to process the
790 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
791 // longer the original filter, but has the remainder matrix baked into it, and passing in the
792 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
793 // to the original filter node (barring inflation from consecutive calls to mapRect). While
794 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
795 // passing getDeviceClipBounds() to 'imageFilter' is correct.
796 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
797 // be important to more accurately calculate the clip bounds in the layer space for the original
798 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500799 SkIRect clipBounds = this->getDeviceClipBounds();
800 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000801 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000802 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000803
reed96e657d2015-03-10 17:30:07 -0700804 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
805
Robert Phillips12078432018-05-17 11:17:39 -0400806 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
807 // If the image filter DAG affects transparent black then we will need to render
808 // out to the clip bounds
809 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000810 }
Robert Phillips12078432018-05-17 11:17:39 -0400811
812 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700813 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700815 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400816 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400818 inputSaveLayerBounds = clipBounds;
819 }
820
821 if (imageFilter) {
822 // expand the clip bounds by the image filter DAG to include extra content that might
823 // be required by the image filters.
824 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
825 SkImageFilter::kReverse_MapDirection,
826 &inputSaveLayerBounds);
827 }
828
829 SkIRect clippedSaveLayerBounds;
830 if (bounds) {
831 // For better or for worse, user bounds currently act as a hard clip on the layer's
832 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
833 clippedSaveLayerBounds = inputSaveLayerBounds;
834 } else {
835 // If there are no user bounds, we don't want to artificially restrict the resulting
836 // layer bounds, so allow the expanded clip bounds free reign.
837 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800839
840 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400841 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800842 if (BoundsAffectsClip(saveLayerFlags)) {
843 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
844 fMCRec->fRasterClip.setEmpty();
845 fDeviceClipBounds.setEmpty();
846 }
847 return false;
848 }
Robert Phillips12078432018-05-17 11:17:39 -0400849 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850
reed4960eee2015-12-18 07:09:18 -0800851 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700852 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400853 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
854 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 }
856
857 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400858 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000859 }
Robert Phillips12078432018-05-17 11:17:39 -0400860
junov@chromium.orga907ac32012-02-24 21:54:07 +0000861 return true;
862}
863
reed4960eee2015-12-18 07:09:18 -0800864int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
865 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000866}
867
Cary Clarke041e312018-03-06 13:00:52 -0500868int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700869 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400870 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
871 // no need for the layer (or any of the draws until the matching restore()
872 this->save();
873 this->clipRect({0,0,0,0});
874 } else {
875 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
876 fSaveCount += 1;
877 this->internalSaveLayer(rec, strategy);
878 }
reed4960eee2015-12-18 07:09:18 -0800879 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800880}
881
Mike Reed148b7fd2018-12-18 17:38:18 -0500882int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
883 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
884 // Assuming clips never expand, if the request bounds is outside of the current clip
885 // there is no need to copy/restore the area, so just devolve back to a regular save.
886 this->save();
887 } else {
888 bool doTheWork = this->onDoSaveBehind(bounds);
889 fSaveCount += 1;
890 this->internalSave();
891 if (doTheWork) {
892 this->internalSaveBehind(bounds);
893 }
894 }
895 return this->getSaveCount() - 1;
896}
897
reeda2217ef2016-07-20 06:04:34 -0700898void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500899 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500900 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400901 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
902 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400903 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500904 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
905 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400906 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400907 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
908 // This means that we only have to copy a dst-sized block of pixels out of src and translate
909 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400910 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
911 dstOrigin.y() - src->getOrigin().y(),
912 dst->width(), dst->height());
913 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400914 return;
915 }
916
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400917 auto special = src->snapSpecial(snapBounds);
918 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400919 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
920 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400921 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
922 }
923 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400924 }
925
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400926 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
927 // by the backdrop filter.
928 SkMatrix toRoot, layerMatrix;
929 SkSize scale;
930 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
931 toRoot = SkMatrix::I();
932 layerMatrix = ctm;
933 } else if (ctm.decomposeScale(&scale, &toRoot)) {
934 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
935 } else {
936 // Perspective, for now, do no scaling of the layer itself.
937 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
938 // the matrix, e.g. based on the midpoint of the near/far planes?
939 toRoot = ctm;
940 layerMatrix = SkMatrix::I();
941 }
942
943 // We have to map the dst bounds from the root space into the layer space where filtering will
944 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
945 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
946 // is a safe, conservative estimate.
947 SkMatrix fromRoot;
948 if (!toRoot.invert(&fromRoot)) {
949 return;
950 }
951
952 // This represents what the backdrop filter needs to produce in the layer space, and is sized
953 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
954 SkIRect layerTargetBounds = fromRoot.mapRect(
955 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
956 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
957 // require some extra input pixels.
958 SkIRect layerInputBounds = filter->filterBounds(
959 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
960 &layerTargetBounds);
961
962 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400963 // be the conservative contents required to fill a layerInputBounds-sized surface with the
964 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400965 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
966 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
967 if (!backdropBounds.intersect(srcDevRect)) {
968 return;
969 }
970
971 auto special = src->snapSpecial(backdropBounds);
972 if (!special) {
973 return;
974 }
975
976 SkColorType colorType = src->imageInfo().colorType();
977 if (colorType == kUnknown_SkColorType) {
978 colorType = kRGBA_8888_SkColorType;
979 }
980 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400981
982 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400983 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400984 // Drawing the temporary and final filtered image requires a higher filter quality if the
985 // 'toRoot' transformation is not identity, in order to minimize the impact on already
986 // rendered edges/content.
987 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
988 p.setFilterQuality(kHigh_SkFilterQuality);
989
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400990 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
991 // and stored in a temporary surface, which is then used as the input to the actual filter.
992 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
993 if (!tmpSurface) {
994 return;
995 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400996
997 auto tmpCanvas = tmpSurface->getCanvas();
998 tmpCanvas->clear(SK_ColorTRANSPARENT);
999 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1000 // space, then maps from root space into the layer space, then maps it so the input layer's
1001 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1002 // performed on backdropBounds.
1003 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1004 tmpCanvas->concat(fromRoot);
1005 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001006
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001007 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1008 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1009 special = tmpSurface->makeImageSnapshot();
1010 } else {
1011 // Since there is no extra transform that was done, update the input bounds to reflect
1012 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1013 // was equal to backdropBounds before it was made relative to the src device and cropped.
1014 // When we use the original snapped image directly, just map the update backdrop bounds
1015 // back into the shared layer space
1016 layerInputBounds = backdropBounds;
1017 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001018
1019 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1020 // draw will be 1-1 so there is no need to increase filter quality.
1021 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001022 }
1023
1024 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1025 // layer space. This has to further offset everything so that filter evaluation thinks the
1026 // source image's top left corner is (0, 0).
1027 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1028 // this can be simplified.
1029 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1030 SkMatrix filterCTM = layerMatrix;
1031 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1032 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1033
1034 SkIPoint offset;
1035 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001036 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001037 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1038 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1039 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1040 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001041 offset += layerInputBounds.topLeft();
1042
1043 // Manually setting the device's CTM requires accounting for the device's origin.
1044 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001045 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001046 // a global CTM instead of a device CTM.
1047 SkMatrix dstCTM = toRoot;
1048 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001049 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050
1051 // And because devices don't have a special-image draw function that supports arbitrary
1052 // matrices, we are abusing the asImage() functionality here...
1053 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001054 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001055 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001056 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001057 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1058 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001059 }
robertphillips7354a4b2015-12-16 05:08:27 -08001060}
reed70ee31b2015-12-10 13:44:45 -08001061
Mike Kleine083f7c2018-02-07 12:54:27 -05001062static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001063 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001064 if (prev.bytesPerPixel() <= 4 &&
1065 prev.colorType() != kRGBA_8888_SkColorType &&
1066 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001067 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1068 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1069 ct = kN32_SkColorType;
1070 }
1071 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001072}
1073
reed4960eee2015-12-18 07:09:18 -08001074void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001075 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001076 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001077 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1078
Michael Ludwigeced98b2020-03-03 10:39:41 -05001079 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1080 // saveLayer ignores mask filters, so force it to null
1081 if (paint.get() && paint->getMaskFilter()) {
1082 paint.writable()->setMaskFilter(nullptr);
1083 }
1084
Mike Reed5532c2a2019-02-23 12:00:32 -05001085 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1086 // regardless of any hint-rect from the caller. skbug.com/8783
1087 if (rec.fBackdrop) {
1088 bounds = nullptr;
1089 }
1090
Michael Ludwigeced98b2020-03-03 10:39:41 -05001091 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001092 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001093 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001094
reed8c30a812016-04-20 16:36:51 -07001095 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001096 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1097 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1098 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001099 *
1100 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001101 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1102 * if necessary.
1103 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1104 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001105 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
Michael Ludwig08b260c2019-05-17 11:21:53 -04001106 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001107 * of the original imagefilter, and draw that (via drawSprite)
1108 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1109 *
1110 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1111 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1112 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001113 if (imageFilter) {
1114 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001115 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1116 &modifiedCTM);
1117 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001118 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001119 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001120 modifiedRec = fMCRec;
1121 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001122 imageFilter = modifiedFilter.get();
1123 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001124 }
1125 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1126 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001127 }
reed8c30a812016-04-20 16:36:51 -07001128
junov@chromium.orga907ac32012-02-24 21:54:07 +00001129 // do this before we create the layer. We don't call the public save() since
1130 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001131 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001132
junov@chromium.orga907ac32012-02-24 21:54:07 +00001133 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001134 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001135 if (modifiedRec) {
1136 // In this case there will be no layer in which to stash the matrix so we need to
1137 // revert the prior MCRec to its earlier state.
1138 modifiedRec->fMatrix = stashedMatrix;
1139 }
reed2ff1fce2014-12-11 07:07:37 -08001140 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 }
1142
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001143 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1144 // the clipRectBounds() call above?
1145 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001146 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001147 }
1148
reed8dc0ccb2015-03-20 06:32:52 -07001149 SkPixelGeometry geo = fProps.pixelGeometry();
1150 if (paint) {
reed76033be2015-03-14 10:54:31 -07001151 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001152 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001153 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001154 }
1155 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156
robertphillips5139e502016-07-19 05:10:40 -07001157 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001158 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001159 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001160 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001161 }
reedb2db8982014-11-13 12:41:02 -08001162
Mike Kleine083f7c2018-02-07 12:54:27 -05001163 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001164 if (rec.fSaveLayerFlags & kF16ColorType) {
1165 info = info.makeColorType(kRGBA_F16_SkColorType);
1166 }
reed129ed1c2016-02-22 06:42:31 -08001167
Hal Canary704cd322016-11-07 14:13:52 -05001168 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001169 {
Florin Malita4571e492019-07-16 10:25:58 -04001170 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001171 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001172 const bool trackCoverage =
1173 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001174 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001175 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001176 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001177 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1178 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001179 return;
reed61f501f2015-04-29 08:34:00 -07001180 }
Mike Reedeb1d5a22020-04-14 09:16:40 -04001181 newDevice->setMarkerStack(fMarkerStack.get());
bungeman@google.come25c6842011-08-17 14:53:54 +00001182 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001183 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184
Mike Reedb43a3e02017-02-11 10:18:58 -05001185 // only have a "next" if this new layer doesn't affect the clip (rare)
1186 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 fMCRec->fLayer = layer;
1188 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001189
Mike Reedc61abee2017-02-28 17:45:27 -05001190 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001191 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001192 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001193 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001194
Mike Reedc42a1cd2017-02-14 14:25:14 -05001195 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1196
1197 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1198 if (layer->fNext) {
1199 // need to punch a hole in the previous device, so we don't draw there, given that
1200 // the new top-layer will allow drawing to happen "below" it.
1201 SkRegion hole(ir);
1202 do {
1203 layer = layer->fNext;
1204 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1205 } while (layer->fNext);
1206 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207}
1208
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001209int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001210 if (0xFF == alpha) {
1211 return this->saveLayer(bounds, nullptr);
1212 } else {
1213 SkPaint tmpPaint;
1214 tmpPaint.setAlpha(alpha);
1215 return this->saveLayer(bounds, &tmpPaint);
1216 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001217}
1218
Mike Reed148b7fd2018-12-18 17:38:18 -05001219void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001220 SkBaseDevice* device = this->getTopDevice();
1221 if (nullptr == device) { // Do we still need this check???
1222 return;
1223 }
1224
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001225 // Map the local bounds into the top device's coordinate space (this is not
1226 // necessarily the full global CTM transform).
1227 SkIRect devBounds;
1228 if (localBounds) {
1229 SkRect tmp;
1230 device->localToDevice().mapRect(&tmp, *localBounds);
1231 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1232 devBounds.setEmpty();
1233 }
1234 } else {
1235 devBounds = device->devClipBounds();
1236 }
1237 if (devBounds.isEmpty()) {
1238 return;
1239 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001240
Michael Ludwigac352122019-08-28 21:03:05 +00001241 // This is getting the special image from the current device, which is then drawn into (both by
1242 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1243 // own device, we need to explicitly copy the back image contents so that its original content
1244 // is available when we splat it back later during restore.
1245 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001246 if (!backImage) {
1247 return;
1248 }
1249
1250 // we really need the save, so we can wack the fMCRec
1251 this->checkForDeferredSave();
1252
1253 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1254
1255 SkPaint paint;
1256 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001257 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001258}
1259
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260void SkCanvas::internalRestore() {
1261 SkASSERT(fMCStack.count() != 0);
1262
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001263 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 DeviceCM* layer = fMCRec->fLayer; // may be null
1265 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001266 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267
Mike Reed148b7fd2018-12-18 17:38:18 -05001268 // move this out before we do the actual restore
1269 auto backImage = std::move(fMCRec->fBackImage);
1270
Mike Reedeb1d5a22020-04-14 09:16:40 -04001271 fMarkerStack->restore(fMCRec);
Mike Reedb18e74d2020-01-16 13:58:22 -05001272
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 // now do the normal restore()
1274 fMCRec->~MCRec(); // balanced in save()
1275 fMCStack.pop_back();
1276 fMCRec = (MCRec*)fMCStack.back();
1277
Mike Reedc42a1cd2017-02-14 14:25:14 -05001278 if (fMCRec) {
1279 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1280 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001281
Mike Reed148b7fd2018-12-18 17:38:18 -05001282 if (backImage) {
1283 SkPaint paint;
1284 paint.setBlendMode(SkBlendMode::kDstOver);
1285 const int x = backImage->fLoc.x();
1286 const int y = backImage->fLoc.y();
1287 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1288 nullptr, SkMatrix::I());
1289 }
1290
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1292 since if we're being recorded, we don't want to record this (the
1293 recorder will have already recorded the restore).
1294 */
bsalomon49f085d2014-09-05 13:34:00 -07001295 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001296 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001297 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001298 // At this point, 'layer' has been removed from the device stack, so the devices that
1299 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1300 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001301 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001302 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001303 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001304 delete layer;
reedb679ca82015-04-07 04:40:48 -07001305 } else {
1306 // we're at the root
reeda499f902015-05-01 09:34:31 -07001307 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001308 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001309 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001311 }
msarettfbfa2582016-08-12 08:29:08 -07001312
1313 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001314 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001315 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317}
1318
reede8f30622016-03-23 18:59:25 -07001319sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001320 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001321 props = &fProps;
1322 }
1323 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001324}
1325
reede8f30622016-03-23 18:59:25 -07001326sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001327 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001328 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001329}
1330
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001331SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001332 return this->onImageInfo();
1333}
1334
1335SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001336 SkBaseDevice* dev = this->getDevice();
1337 if (dev) {
1338 return dev->imageInfo();
1339 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001340 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001341 }
1342}
1343
brianosman898235c2016-04-06 07:38:23 -07001344bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001345 return this->onGetProps(props);
1346}
1347
1348bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001349 SkBaseDevice* dev = this->getDevice();
1350 if (dev) {
1351 if (props) {
1352 *props = fProps;
1353 }
1354 return true;
1355 } else {
1356 return false;
1357 }
1358}
1359
reed6ceeebd2016-03-09 14:26:26 -08001360bool SkCanvas::peekPixels(SkPixmap* pmap) {
1361 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001362}
1363
reed884e97c2015-05-26 11:31:54 -07001364bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001365 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001366 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001367}
1368
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001369void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001370 SkPixmap pmap;
1371 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001372 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001373 }
1374 if (info) {
1375 *info = pmap.info();
1376 }
1377 if (rowBytes) {
1378 *rowBytes = pmap.rowBytes();
1379 }
1380 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001381 // If the caller requested the origin, they presumably are expecting the returned pixels to
1382 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1383 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1384 // instead of an origin, just don't expose the pixels in that case. Note that this means
1385 // that layers with complex coordinate spaces can still report their pixels if the caller
1386 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1387 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1388 *origin = this->getTopDevice()->getOrigin();
1389 } else {
1390 return nullptr;
1391 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001392 }
reed884e97c2015-05-26 11:31:54 -07001393 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001394}
1395
reed884e97c2015-05-26 11:31:54 -07001396bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001397 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001398 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001399}
1400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402
Mike Reed8bcd1282019-03-13 16:51:54 -04001403// In our current design/features, we should never have a layer (src) in a different colorspace
1404// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1405// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1406// colorspace.
1407static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1408 SkASSERT(src == dst);
1409}
1410
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001411void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001412 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001414 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 paint = &tmp;
1416 }
reed@google.com4b226022011-01-11 18:32:13 +00001417
Mike Reed38992392019-07-30 10:48:15 -04001418 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001419
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001421 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001422 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1423 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001424 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001425 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001426 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1427 // so it should always be possible to use the relative origin. Once drawDevice() and
1428 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1429 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001430 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001431 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1432 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001433 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1434 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001435 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1436 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001437 }
reed@google.com76dd2772012-01-05 21:15:07 +00001438 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001439 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001440 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 }
reeda2217ef2016-07-20 06:04:34 -07001442
Mike Reed38992392019-07-30 10:48:15 -04001443 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444}
1445
reed32704672015-12-16 08:27:10 -08001446/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001447
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001448void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001449 if (dx || dy) {
1450 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001451 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001452
reedfe69b502016-09-12 06:31:48 -07001453 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001454 // However, if either is non-finite, we might still complicate the matrix type,
1455 // so we still have to compute this.
1456 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001457
Mike Reedc42a1cd2017-02-14 14:25:14 -05001458 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001459
reedfe69b502016-09-12 06:31:48 -07001460 this->didTranslate(dx,dy);
1461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001464void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001465 if (sx != 1 || sy != 1) {
1466 this->checkForDeferredSave();
1467 fMCRec->fMatrix.preScale(sx, sy);
1468
1469 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1470 // but pre-scaling by a non-finite does change it, so we have to recompute.
1471 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1472
1473 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1474
1475 this->didScale(sx, sy);
1476 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001479void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001480 SkMatrix m;
1481 m.setRotate(degrees);
1482 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483}
1484
bungeman7438bfc2016-07-12 15:01:19 -07001485void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1486 SkMatrix m;
1487 m.setRotate(degrees, px, py);
1488 this->concat(m);
1489}
1490
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001491void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001492 SkMatrix m;
1493 m.setSkew(sx, sy);
1494 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001495}
1496
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001497void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001498 if (matrix.isIdentity()) {
1499 return;
1500 }
1501
reed2ff1fce2014-12-11 07:07:37 -08001502 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001503 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001504
msarett9637ea92016-08-18 14:03:30 -07001505 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001506
Mike Reed7627fa52017-02-08 10:07:53 -05001507 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001508
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001509 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001510}
1511
Mike Reed2f92c932020-04-06 15:44:17 -04001512void SkCanvas::internalConcat44(const SkM44& m) {
Mike Reed403c8072020-01-08 10:40:39 -05001513 this->checkForDeferredSave();
1514
Mike Reed3ef77dd2020-04-06 10:41:09 -04001515 fMCRec->fMatrix.preConcat(m);
Mike Reed403c8072020-01-08 10:40:39 -05001516
1517 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1518
1519 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed2f92c932020-04-06 15:44:17 -04001520}
Mike Reed403c8072020-01-08 10:40:39 -05001521
Mike Reed2f92c932020-04-06 15:44:17 -04001522void SkCanvas::concat(const SkM44& m) {
1523 this->internalConcat44(m);
Mike Reeda735ad92020-04-06 21:32:43 -04001524 // notify subclasses
1525#ifdef SK_SUPPORT_LEGACY_DIDCONCAT44
1526 this->didConcat44(SkMatrixPriv::M44ColMajor(m));
1527#else
1528 this->didConcat44(m);
1529#endif
Mike Reedee3216d2020-01-17 17:35:04 -05001530}
1531
reed8c30a812016-04-20 16:36:51 -07001532void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001533 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001534 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001535
Mike Reedc42a1cd2017-02-14 14:25:14 -05001536 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001537}
1538
1539void SkCanvas::setMatrix(const SkMatrix& matrix) {
1540 this->checkForDeferredSave();
1541 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001542 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543}
1544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001546 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547}
1548
Mike Reed7fe6ee32020-04-09 12:35:09 -04001549void SkCanvas::markCTM(MarkerID id) {
1550 if (id == 0) return;
1551
1552 this->onMarkCTM(id);
1553
Mike Reedeb1d5a22020-04-14 09:16:40 -04001554 fMarkerStack->setMarker(id, this->getLocalToDevice(), fMCRec);
Mike Reed7fe6ee32020-04-09 12:35:09 -04001555}
1556
1557bool SkCanvas::findMarkedCTM(MarkerID id, SkM44* mx) const {
Mike Reedeb1d5a22020-04-14 09:16:40 -04001558 return fMarkerStack->findMarker(id, mx);
Mike Reed7fe6ee32020-04-09 12:35:09 -04001559}
1560
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561//////////////////////////////////////////////////////////////////////////////
1562
Mike Reedc1f77742016-12-09 09:00:50 -05001563void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001564 if (!rect.isFinite()) {
1565 return;
1566 }
reed2ff1fce2014-12-11 07:07:37 -08001567 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001568 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1569 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001570}
1571
Mike Reedc1f77742016-12-09 09:00:50 -05001572void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001573 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001574
Mike Reed7627fa52017-02-08 10:07:53 -05001575 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001576
reedc64eff52015-11-21 12:39:45 -08001577 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001578 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1579 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001580 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581}
1582
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001583void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1584 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001585 if (fClipRestrictionRect.isEmpty()) {
1586 // we notify the device, but we *dont* resolve deferred saves (since we're just
1587 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001588 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001589 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001590 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001591 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001592 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001593 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001594 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1595 }
1596}
1597
Mike Reedc1f77742016-12-09 09:00:50 -05001598void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001599 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001601 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001602 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1603 } else {
1604 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001605 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001606}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001607
Mike Reedc1f77742016-12-09 09:00:50 -05001608void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001609 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001610
Brian Salomona3b45d42016-10-03 11:36:16 -04001611 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001612
Mike Reed7627fa52017-02-08 10:07:53 -05001613 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001614
Mike Reed20800c82017-11-15 16:09:04 -05001615 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1616 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001617 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001618}
1619
Mike Reedc1f77742016-12-09 09:00:50 -05001620void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001621 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001623
1624 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1625 SkRect r;
1626 if (path.isRect(&r)) {
1627 this->onClipRect(r, op, edgeStyle);
1628 return;
1629 }
1630 SkRRect rrect;
1631 if (path.isOval(&r)) {
1632 rrect.setOval(r);
1633 this->onClipRRect(rrect, op, edgeStyle);
1634 return;
1635 }
1636 if (path.isRRect(&rrect)) {
1637 this->onClipRRect(rrect, op, edgeStyle);
1638 return;
1639 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 }
robertphillips39f05382015-11-24 09:30:12 -08001641
1642 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001643}
1644
Mike Reedc1f77742016-12-09 09:00:50 -05001645void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001646 AutoValidateClip avc(this);
1647
Brian Salomona3b45d42016-10-03 11:36:16 -04001648 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001649
Mike Reed7627fa52017-02-08 10:07:53 -05001650 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651
Brian Salomona3b45d42016-10-03 11:36:16 -04001652 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001653 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001654 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001655 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656}
1657
Mike Reed121c2af2020-03-10 14:02:56 -04001658void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1659 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001660 if (sh->isOpaque()) {
1661 if (op == SkClipOp::kIntersect) {
1662 // we don't occlude anything, so skip this call
1663 } else {
1664 SkASSERT(op == SkClipOp::kDifference);
1665 // we occlude everything, so set the clip to empty
1666 this->clipRect({0,0,0,0});
1667 }
1668 } else {
1669 this->onClipShader(std::move(sh), op);
1670 }
Mike Reed121c2af2020-03-10 14:02:56 -04001671 }
1672}
1673
1674void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1675 AutoValidateClip avc(this);
1676
1677 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1678
1679 // we don't know how to mutate our conservative bounds, so we don't
1680}
1681
Mike Reedc1f77742016-12-09 09:00:50 -05001682void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001683 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001684 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001685}
1686
Mike Reedc1f77742016-12-09 09:00:50 -05001687void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001688 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001689
reed@google.com5c3d1472011-02-22 19:12:23 +00001690 AutoValidateClip avc(this);
1691
Mike Reed20800c82017-11-15 16:09:04 -05001692 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001693 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694}
1695
reed@google.com819c9212011-02-23 18:56:55 +00001696#ifdef SK_DEBUG
1697void SkCanvas::validateClip() const {
1698 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001699 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001700 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001701 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001702 return;
1703 }
reed@google.com819c9212011-02-23 18:56:55 +00001704}
1705#endif
1706
Mike Reeda1361362017-03-07 09:37:29 -05001707bool SkCanvas::androidFramework_isClipAA() const {
1708 bool containsAA = false;
1709
1710 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1711
1712 return containsAA;
1713}
1714
1715class RgnAccumulator {
1716 SkRegion* fRgn;
1717public:
1718 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1719 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1720 SkIPoint origin = device->getOrigin();
1721 if (origin.x() | origin.y()) {
1722 rgn->translate(origin.x(), origin.y());
1723 }
1724 fRgn->op(*rgn, SkRegion::kUnion_Op);
1725 }
1726};
1727
1728void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1729 RgnAccumulator accum(rgn);
1730 SkRegion tmp;
1731
1732 rgn->setEmpty();
1733 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001734}
1735
reed@google.com5c3d1472011-02-22 19:12:23 +00001736///////////////////////////////////////////////////////////////////////////////
1737
reed@google.com754de5f2014-02-24 19:38:20 +00001738bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001739 return fMCRec->fRasterClip.isEmpty();
1740
1741 // TODO: should we only use the conservative answer in a recording canvas?
1742#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001743 SkBaseDevice* dev = this->getTopDevice();
1744 // if no device we return true
1745 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001746#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001747}
1748
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001749bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001750 SkBaseDevice* dev = this->getTopDevice();
1751 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001752 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001753}
1754
msarettfbfa2582016-08-12 08:29:08 -07001755static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1756#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1757 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1758 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1759 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1760 return 0xF != _mm_movemask_ps(mask);
1761#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1762 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1763 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1764 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1765 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1766#else
1767 SkRect devRectAsRect;
1768 SkRect devClipAsRect;
1769 devRect.store(&devRectAsRect.fLeft);
1770 devClip.store(&devClipAsRect.fLeft);
1771 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1772#endif
1773}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001774
msarettfbfa2582016-08-12 08:29:08 -07001775// It's important for this function to not be inlined. Otherwise the compiler will share code
1776// between the fast path and the slow path, resulting in two slow paths.
1777static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1778 const SkMatrix& matrix) {
1779 SkRect deviceRect;
1780 matrix.mapRect(&deviceRect, src);
1781 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1782}
1783
1784bool SkCanvas::quickReject(const SkRect& src) const {
1785#ifdef SK_DEBUG
1786 // Verify that fDeviceClipBounds are set properly.
1787 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001788 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001789 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001790 } else {
msarettfbfa2582016-08-12 08:29:08 -07001791 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 }
msarettfbfa2582016-08-12 08:29:08 -07001793
msarett9637ea92016-08-18 14:03:30 -07001794 // Verify that fIsScaleTranslate is set properly.
1795 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001796#endif
1797
msarett9637ea92016-08-18 14:03:30 -07001798 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001799 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1800 }
1801
1802 // We inline the implementation of mapScaleTranslate() for the fast path.
1803 float sx = fMCRec->fMatrix.getScaleX();
1804 float sy = fMCRec->fMatrix.getScaleY();
1805 float tx = fMCRec->fMatrix.getTranslateX();
1806 float ty = fMCRec->fMatrix.getTranslateY();
1807 Sk4f scale(sx, sy, sx, sy);
1808 Sk4f trans(tx, ty, tx, ty);
1809
1810 // Apply matrix.
1811 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1812
1813 // Make sure left < right, top < bottom.
1814 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1815 Sk4f min = Sk4f::Min(ltrb, rblt);
1816 Sk4f max = Sk4f::Max(ltrb, rblt);
1817 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1818 // ARM this sequence generates the fastest (a single instruction).
1819 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1820
1821 // Check if the device rect is NaN or outside the clip.
1822 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823}
1824
reed@google.com3b3e8952012-08-16 20:53:31 +00001825bool SkCanvas::quickReject(const SkPath& path) const {
1826 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827}
1828
Mike Klein83c8dd92017-11-28 17:08:45 -05001829SkRect SkCanvas::getLocalClipBounds() const {
1830 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001831 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001832 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 }
1834
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001835 SkMatrix inverse;
1836 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001837 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001838 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001839 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840
Mike Reed42e8c532017-01-23 14:09:13 -05001841 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001842 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001843 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001844
Mike Reedb57b9312018-04-23 12:12:54 -04001845 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001846 inverse.mapRect(&bounds, r);
1847 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848}
1849
Mike Klein83c8dd92017-11-28 17:08:45 -05001850SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001851 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001852}
1853
Mike Reedb18e74d2020-01-16 13:58:22 -05001854///////////////////////////////////////////////////////////////////////
1855
Mike Reed403c8072020-01-08 10:40:39 -05001856SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001857 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858}
1859
Mike Reed46f5c5f2020-02-20 15:42:29 -05001860SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001861 return fMCRec->fMatrix;
1862}
1863
Brian Osman11052242016-10-27 14:47:55 -04001864GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001865 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001866 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001867}
1868
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001869GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001870 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001871 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001872}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001873
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001874void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1875 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001876 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001877 if (outer.isEmpty()) {
1878 return;
1879 }
1880 if (inner.isEmpty()) {
1881 this->drawRRect(outer, paint);
1882 return;
1883 }
1884
1885 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001886 // be able to return ...
1887 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001888 //
1889 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001890 if (!outer.getBounds().contains(inner.getBounds())) {
1891 return;
1892 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001893
1894 this->onDrawDRRect(outer, inner, paint);
1895}
1896
reed41af9662015-01-05 07:49:08 -08001897void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001898 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001899 this->onDrawPaint(paint);
1900}
1901
1902void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001903 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001904 // To avoid redundant logic in our culling code and various backends, we always sort rects
1905 // before passing them along.
1906 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001907}
1908
Mike Reedd5674082019-04-19 15:00:47 -04001909void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1910 TRACE_EVENT0("skia", TRACE_FUNC);
1911 this->onDrawBehind(paint);
1912}
1913
msarettdca352e2016-08-26 06:37:45 -07001914void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001915 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001916 if (region.isEmpty()) {
1917 return;
1918 }
1919
1920 if (region.isRect()) {
1921 return this->drawIRect(region.getBounds(), paint);
1922 }
1923
1924 this->onDrawRegion(region, paint);
1925}
1926
reed41af9662015-01-05 07:49:08 -08001927void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001928 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001929 // To avoid redundant logic in our culling code and various backends, we always sort rects
1930 // before passing them along.
1931 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001932}
1933
1934void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001935 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001936 this->onDrawRRect(rrect, paint);
1937}
1938
1939void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001940 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001941 this->onDrawPoints(mode, count, pts, paint);
1942}
1943
Mike Reede88a1cb2017-03-17 09:50:46 -04001944void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1945 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001946 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001947}
1948
1949void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001950 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001951 RETURN_ON_NULL(vertices);
Brian Osmanf11e3312020-03-24 14:57:38 -04001952
Mike Reed5caf9352020-03-02 14:57:09 -05001953 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Brian Osman8cbedf92020-03-31 10:38:31 -04001954 SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);
Brian Osmanf11e3312020-03-24 14:57:38 -04001955
Brian Osmand1afef62020-04-09 16:24:23 -04001956 // If the vertices contain custom attributes, ensure they line up with the paint's shader.
Brian Osmanf11e3312020-03-24 14:57:38 -04001957 const SkRuntimeEffect* effect =
1958 paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001959 if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) {
Brian Osmanf11e3312020-03-24 14:57:38 -04001960 return;
1961 }
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001962 if (effect) {
1963 int attrIndex = 0;
1964 for (const auto& v : effect->varyings()) {
Brian Osmand1afef62020-04-09 16:24:23 -04001965 const SkVertices::Attribute& attr(vertices->priv().attributes()[attrIndex++]);
1966 // Mismatch between the SkSL varying and the vertex shader output for this attribute
1967 if (attr.channelCount() != v.fWidth) {
1968 return;
1969 }
1970 // If we can't provide any of the asked-for matrices, we can't draw this
1971 if (attr.fMarkerID && !this->findMarkedCTM(attr.fMarkerID, nullptr)) {
Brian Osmanffd11f4a2020-03-30 09:57:53 -04001972 return;
1973 }
1974 }
1975 }
Brian Osmanf11e3312020-03-24 14:57:38 -04001976
Mike Reed5caf9352020-03-02 14:57:09 -05001977 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001978}
1979
1980void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001981 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001982 this->onDrawPath(path, paint);
1983}
1984
reeda85d4d02015-05-06 12:56:48 -07001985void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001986 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001987 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001988 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001989}
1990
Mike Reedc4e31092018-01-30 11:15:27 -05001991// Returns true if the rect can be "filled" : non-empty and finite
1992static bool fillable(const SkRect& r) {
1993 SkScalar w = r.width();
1994 SkScalar h = r.height();
1995 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1996}
1997
reede47829b2015-08-06 10:02:53 -07001998void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1999 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002000 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002001 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002002 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002003 return;
2004 }
2005 this->onDrawImageRect(image, &src, dst, paint, constraint);
2006}
reed41af9662015-01-05 07:49:08 -08002007
reed84984ef2015-07-17 07:09:43 -07002008void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2009 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002010 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002011 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002012}
2013
Brian Salomonf08002c2018-10-26 16:15:46 -04002014void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002015 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002016 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002017 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002018}
reede47829b2015-08-06 10:02:53 -07002019
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002020namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002021class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002022public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002023 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2024 if (!origPaint) {
2025 return;
2026 }
2027 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2028 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2029 }
2030 if (origPaint->getMaskFilter()) {
2031 fPaint.writable()->setMaskFilter(nullptr);
2032 }
2033 if (origPaint->isAntiAlias()) {
2034 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002035 }
2036 }
2037
2038 const SkPaint* get() const {
2039 return fPaint;
2040 }
2041
2042private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002043 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002044};
2045} // namespace
2046
reed4c21dc52015-06-25 12:32:03 -07002047void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2048 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002049 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002050 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002051 if (dst.isEmpty()) {
2052 return;
2053 }
msarett552bca92016-08-03 06:53:26 -07002054 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002055 LatticePaint latticePaint(paint);
2056 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002057 } else {
reede47829b2015-08-06 10:02:53 -07002058 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002059 }
reed4c21dc52015-06-25 12:32:03 -07002060}
2061
msarett16882062016-08-16 09:31:08 -07002062void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2063 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002064 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002065 RETURN_ON_NULL(image);
2066 if (dst.isEmpty()) {
2067 return;
2068 }
msarett71df2d72016-09-30 12:41:42 -07002069
2070 SkIRect bounds;
2071 Lattice latticePlusBounds = lattice;
2072 if (!latticePlusBounds.fBounds) {
2073 bounds = SkIRect::MakeWH(image->width(), image->height());
2074 latticePlusBounds.fBounds = &bounds;
2075 }
2076
2077 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002078 LatticePaint latticePaint(paint);
2079 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002080 } else {
2081 this->drawImageRect(image, dst, paint);
2082 }
2083}
2084
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002085static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002086 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002087 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002088 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002089 return SkImage::MakeFromBitmap(bitmap);
2090}
2091
2092void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2093 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002094}
2095
reede47829b2015-08-06 10:02:53 -07002096void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002097 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002098 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002099}
2100
reed84984ef2015-07-17 07:09:43 -07002101void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2102 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002103 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002104}
2105
reede47829b2015-08-06 10:02:53 -07002106void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2107 SrcRectConstraint constraint) {
2108 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2109 constraint);
2110}
reede47829b2015-08-06 10:02:53 -07002111
reed71c3c762015-06-24 10:29:17 -07002112void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002113 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002114 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002115 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002116 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002117 if (count <= 0) {
2118 return;
2119 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002120 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002121 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002122 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002123}
2124
reedf70b5312016-03-04 16:36:20 -08002125void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002126 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002127 if (key) {
2128 this->onDrawAnnotation(rect, key, value);
2129 }
2130}
2131
reede47829b2015-08-06 10:02:53 -07002132void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2133 const SkPaint* paint, SrcRectConstraint constraint) {
2134 if (src) {
2135 this->drawImageRect(image, *src, dst, paint, constraint);
2136 } else {
2137 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2138 dst, paint, constraint);
2139 }
2140}
reede47829b2015-08-06 10:02:53 -07002141
Mike Reed4204da22017-05-17 08:53:36 -04002142void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002143 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002144 this->onDrawShadowRec(path, rec);
2145}
2146
2147void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2148 SkPaint paint;
2149 const SkRect& pathBounds = path.getBounds();
2150
Mike Reed38992392019-07-30 10:48:15 -04002151 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002152 while (iter.next()) {
2153 iter.fDevice->drawShadow(path, rec);
2154 }
Mike Reed38992392019-07-30 10:48:15 -04002155 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002156}
2157
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002158void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002159 QuadAAFlags aaFlags, const SkColor4f& color,
2160 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002161 TRACE_EVENT0("skia", TRACE_FUNC);
2162 // Make sure the rect is sorted before passing it along
2163 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2164}
2165
2166void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2167 const SkPoint dstClips[],
2168 const SkMatrix preViewMatrices[],
2169 const SkPaint* paint,
2170 SrcRectConstraint constraint) {
2171 TRACE_EVENT0("skia", TRACE_FUNC);
2172 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2173}
2174
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175//////////////////////////////////////////////////////////////////////////////
2176// These are the virtual drawing methods
2177//////////////////////////////////////////////////////////////////////////////
2178
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002179void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002180 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002181 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2182 }
2183}
2184
reed41af9662015-01-05 07:49:08 -08002185void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002186 this->internalDrawPaint(paint);
2187}
2188
2189void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002190 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191
2192 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002193 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 }
2195
Mike Reed38992392019-07-30 10:48:15 -04002196 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197}
2198
reed41af9662015-01-05 07:49:08 -08002199void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2200 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 if ((long)count <= 0) {
2202 return;
2203 }
2204
Mike Reed822128b2017-02-28 16:41:03 -05002205 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002206 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002207 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002208 // special-case 2 points (common for drawing a single line)
2209 if (2 == count) {
2210 r.set(pts[0], pts[1]);
2211 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002212 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002213 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002214 if (!r.isFinite()) {
2215 return;
2216 }
Mike Reed822128b2017-02-28 16:41:03 -05002217 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002218 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2219 return;
2220 }
2221 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002222 }
reed@google.coma584aed2012-05-16 14:06:02 +00002223
halcanary96fcdcc2015-08-27 07:41:13 -07002224 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225
Mike Reed38992392019-07-30 10:48:15 -04002226 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002227
reed@android.com8a1c16f2008-12-17 15:59:43 +00002228 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002229 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230 }
reed@google.com4b226022011-01-11 18:32:13 +00002231
Mike Reed38992392019-07-30 10:48:15 -04002232 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233}
2234
reed4a167172016-08-18 17:15:25 -07002235static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002236 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002237}
2238
reed41af9662015-01-05 07:49:08 -08002239void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002240 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002241 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002242 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002243 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002244 return;
2245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246 }
reed@google.com4b226022011-01-11 18:32:13 +00002247
reed4a167172016-08-18 17:15:25 -07002248 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002249 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250
reed4a167172016-08-18 17:15:25 -07002251 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002252 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002253 }
2254
Mike Reed38992392019-07-30 10:48:15 -04002255 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002256 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002257 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002258 SkDrawIter iter(this);
2259 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002260 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263}
2264
msarett44df6512016-08-25 13:54:30 -07002265void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002266 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002267 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002268 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002269 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2270 return;
2271 }
msarett44df6512016-08-25 13:54:30 -07002272 }
2273
Mike Reed38992392019-07-30 10:48:15 -04002274 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002275
2276 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002277 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002278 }
2279
Mike Reed38992392019-07-30 10:48:15 -04002280 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002281}
2282
Mike Reedd5674082019-04-19 15:00:47 -04002283void SkCanvas::onDrawBehind(const SkPaint& paint) {
2284 SkIRect bounds;
2285 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2286 for (;;) {
2287 const MCRec* rec = (const MCRec*)iter.prev();
2288 if (!rec) {
2289 return; // no backimages, so nothing to draw
2290 }
2291 if (rec->fBackImage) {
2292 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2293 rec->fBackImage->fImage->width(),
2294 rec->fBackImage->fImage->height());
2295 break;
2296 }
2297 }
2298
Mike Reed38992392019-07-30 10:48:15 -04002299 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002300
2301 while (iter.next()) {
2302 SkBaseDevice* dev = iter.fDevice;
2303
Mike Reedd5674082019-04-19 15:00:47 -04002304 dev->save();
2305 // We use clipRegion because it is already defined to operate in dev-space
2306 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2307 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002308 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002309 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002310 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002311 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002312 }
2313
Mike Reed38992392019-07-30 10:48:15 -04002314 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002315}
2316
reed41af9662015-01-05 07:49:08 -08002317void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002318 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002319 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002320 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002321 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002322 return;
2323 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002324 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002325
Mike Reed38992392019-07-30 10:48:15 -04002326 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002327
2328 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002329 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002330 }
2331
Mike Reed38992392019-07-30 10:48:15 -04002332 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002333}
2334
bsalomonac3aa242016-08-19 11:25:19 -07002335void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2336 SkScalar sweepAngle, bool useCenter,
2337 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002338 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002339 if (paint.canComputeFastBounds()) {
2340 SkRect storage;
2341 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002342 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002343 return;
2344 }
bsalomonac3aa242016-08-19 11:25:19 -07002345 }
2346
Mike Reed38992392019-07-30 10:48:15 -04002347 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002348
2349 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002350 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002351 }
2352
Mike Reed38992392019-07-30 10:48:15 -04002353 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002354}
2355
reed41af9662015-01-05 07:49:08 -08002356void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002357 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002358 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002359 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2360 return;
2361 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002362 }
2363
2364 if (rrect.isRect()) {
2365 // call the non-virtual version
2366 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002367 return;
2368 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002369 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002370 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2371 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002372 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002373
Mike Reed38992392019-07-30 10:48:15 -04002374 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002375
2376 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002377 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002378 }
2379
Mike Reed38992392019-07-30 10:48:15 -04002380 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002381}
2382
Mike Reed822128b2017-02-28 16:41:03 -05002383void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002384 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002385 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002386 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2387 return;
2388 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002389 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002390
Mike Reed38992392019-07-30 10:48:15 -04002391 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002392
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002393 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002394 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002395 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002396
Mike Reed38992392019-07-30 10:48:15 -04002397 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002398}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002399
reed41af9662015-01-05 07:49:08 -08002400void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002401 if (!path.isFinite()) {
2402 return;
2403 }
2404
Mike Reed822128b2017-02-28 16:41:03 -05002405 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002406 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002407 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002408 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2409 return;
2410 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002412
Mike Reed822128b2017-02-28 16:41:03 -05002413 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002414 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002415 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002416 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002417 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002418 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419
Mike Reed38992392019-07-30 10:48:15 -04002420 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002421
2422 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002423 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002424 }
2425
Mike Reed38992392019-07-30 10:48:15 -04002426 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002427}
2428
reed262a71b2015-12-05 13:07:27 -08002429bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002430 if (!paint.getImageFilter()) {
2431 return false;
2432 }
2433
2434 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002435 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002436 return false;
2437 }
2438
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002439 // The other paint effects need to be applied before the image filter, but the sprite draw
2440 // applies the filter explicitly first.
2441 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2442 return false;
2443 }
reed262a71b2015-12-05 13:07:27 -08002444 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2445 // Once we can filter and the filter will return a result larger than itself, we should be
2446 // able to remove this constraint.
2447 // skbug.com/4526
2448 //
2449 SkPoint pt;
2450 ctm.mapXY(x, y, &pt);
2451 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2452 return ir.contains(fMCRec->fRasterClip.getBounds());
2453}
2454
Mike Reedf441cfc2018-04-11 14:50:16 -04002455// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2456// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2457// null.
2458static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2459 if (paintParam) {
2460 *real = *paintParam;
2461 real->setStyle(SkPaint::kFill_Style);
2462 real->setPathEffect(nullptr);
2463 paintParam = real;
2464 }
2465 return paintParam;
2466}
2467
reeda85d4d02015-05-06 12:56:48 -07002468void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002469 SkPaint realPaint;
2470 paint = init_image_paint(&realPaint, paint);
2471
reeda85d4d02015-05-06 12:56:48 -07002472 SkRect bounds = SkRect::MakeXYWH(x, y,
2473 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002474 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002475 SkRect tmp = bounds;
2476 if (paint) {
2477 paint->computeFastBounds(tmp, &tmp);
2478 }
2479 if (this->quickReject(tmp)) {
2480 return;
2481 }
reeda85d4d02015-05-06 12:56:48 -07002482 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002483 // At this point we need a real paint object. If the caller passed null, then we should
2484 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2485 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2486 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002487
reeda2217ef2016-07-20 06:04:34 -07002488 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002489 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2490 *paint);
2491 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002492 special = this->getDevice()->makeSpecial(image);
2493 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002494 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002495 }
2496 }
2497
Mike Reed38992392019-07-30 10:48:15 -04002498 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002499
reeda85d4d02015-05-06 12:56:48 -07002500 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002501 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002502 if (special) {
2503 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002504 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002505 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002506 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002507 SkScalarRoundToInt(pt.fY), pnt,
2508 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002509 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002510 iter.fDevice->drawImageRect(
2511 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2512 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002513 }
reeda85d4d02015-05-06 12:56:48 -07002514 }
halcanary9d524f22016-03-29 09:03:52 -07002515
Mike Reed38992392019-07-30 10:48:15 -04002516 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002517}
2518
reed41af9662015-01-05 07:49:08 -08002519void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002520 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002521 SkPaint realPaint;
2522 paint = init_image_paint(&realPaint, paint);
2523
halcanary96fcdcc2015-08-27 07:41:13 -07002524 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002525 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002526 if (paint) {
2527 paint->computeFastBounds(dst, &storage);
2528 }
2529 if (this->quickReject(storage)) {
2530 return;
2531 }
reeda85d4d02015-05-06 12:56:48 -07002532 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002533 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002534
Mike Reed38992392019-07-30 10:48:15 -04002535 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002536
reeda85d4d02015-05-06 12:56:48 -07002537 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002538 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002539 }
halcanary9d524f22016-03-29 09:03:52 -07002540
Mike Reed38992392019-07-30 10:48:15 -04002541 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002542}
2543
reed4c21dc52015-06-25 12:32:03 -07002544void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2545 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002546 SkPaint realPaint;
2547 paint = init_image_paint(&realPaint, paint);
2548
halcanary96fcdcc2015-08-27 07:41:13 -07002549 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002550 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002551 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2552 return;
2553 }
reed@google.com3d608122011-11-21 15:16:16 +00002554 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002555 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002556
Mike Reed38992392019-07-30 10:48:15 -04002557 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002558
reed4c21dc52015-06-25 12:32:03 -07002559 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002560 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002561 }
halcanary9d524f22016-03-29 09:03:52 -07002562
Mike Reed38992392019-07-30 10:48:15 -04002563 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002564}
2565
msarett16882062016-08-16 09:31:08 -07002566void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2567 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002568 SkPaint realPaint;
2569 paint = init_image_paint(&realPaint, paint);
2570
msarett16882062016-08-16 09:31:08 -07002571 if (nullptr == paint || paint->canComputeFastBounds()) {
2572 SkRect storage;
2573 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2574 return;
2575 }
2576 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002577 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002578
Mike Reed38992392019-07-30 10:48:15 -04002579 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002580
2581 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002582 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002583 }
2584
Mike Reed38992392019-07-30 10:48:15 -04002585 DRAW_END
msarett16882062016-08-16 09:31:08 -07002586}
2587
fmalita00d5c2c2014-08-21 08:53:26 -07002588void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2589 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002590 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002591 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002592 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002593 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002594 SkRect tmp;
2595 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2596 return;
2597 }
2598 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002599 }
2600
fmalita024f9962015-03-03 19:08:17 -08002601 // We cannot filter in the looper as we normally do, because the paint is
2602 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002603 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002604
fmalitaaa1b9122014-08-28 14:32:24 -07002605 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002606 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002607 }
2608
Mike Reed38992392019-07-30 10:48:15 -04002609 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002610}
2611
Mike Reed358fcad2018-11-23 15:27:51 -05002612// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002613void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002614 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2615 TRACE_EVENT0("skia", TRACE_FUNC);
2616 if (byteLength) {
2617 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002618 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002619 }
2620}
Mike Reed4f81bb72019-01-23 09:23:00 -05002621
fmalita00d5c2c2014-08-21 08:53:26 -07002622void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2623 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002624 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002625 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002626 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002627 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002628}
reed@google.come0d9ce82014-04-23 04:00:17 +00002629
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002630void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2631 const SkPaint& paint) {
2632 DRAW_BEGIN(paint, nullptr)
2633
2634 while (iter.next()) {
2635 // In the common case of one iteration we could std::move vertices here.
2636 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2637 }
2638
2639 DRAW_END
2640}
Brian Salomon199fb872017-02-06 09:41:10 -05002641
dandovb3c9d1c2014-08-12 08:34:29 -07002642void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002643 const SkPoint texCoords[4], SkBlendMode bmode,
2644 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002645 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002646 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002647 return;
2648 }
mtklein6cfa73a2014-08-13 13:33:49 -07002649
Mike Reedfaba3712016-11-03 14:45:31 -04002650 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002651}
2652
2653void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002654 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002655 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002656 // Since a patch is always within the convex hull of the control points, we discard it when its
2657 // bounding rectangle is completely outside the current clip.
2658 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002659 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002660 if (this->quickReject(bounds)) {
2661 return;
2662 }
mtklein6cfa73a2014-08-13 13:33:49 -07002663
Mike Reed38992392019-07-30 10:48:15 -04002664 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002665
dandovecfff212014-08-04 10:02:00 -07002666 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002667 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002668 }
mtklein6cfa73a2014-08-13 13:33:49 -07002669
Mike Reed38992392019-07-30 10:48:15 -04002670 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002671}
2672
reeda8db7282015-07-07 10:22:31 -07002673void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002674#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002675 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002676#endif
reede3b38ce2016-01-08 09:18:44 -08002677 RETURN_ON_NULL(dr);
2678 if (x || y) {
2679 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2680 this->onDrawDrawable(dr, &matrix);
2681 } else {
2682 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002683 }
2684}
2685
reeda8db7282015-07-07 10:22:31 -07002686void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002687#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002688 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002689#endif
reede3b38ce2016-01-08 09:18:44 -08002690 RETURN_ON_NULL(dr);
2691 if (matrix && matrix->isIdentity()) {
2692 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002693 }
reede3b38ce2016-01-08 09:18:44 -08002694 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002695}
2696
2697void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002698 // drawable bounds are no longer reliable (e.g. android displaylist)
2699 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002700 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002701}
2702
reed71c3c762015-06-24 10:29:17 -07002703void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002704 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002705 const SkRect* cull, const SkPaint* paint) {
2706 if (cull && this->quickReject(*cull)) {
2707 return;
2708 }
2709
2710 SkPaint pnt;
2711 if (paint) {
2712 pnt = *paint;
2713 }
halcanary9d524f22016-03-29 09:03:52 -07002714
Mike Reed38992392019-07-30 10:48:15 -04002715 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002716 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002717 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002718 }
Mike Reed38992392019-07-30 10:48:15 -04002719 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002720}
2721
reedf70b5312016-03-04 16:36:20 -08002722void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2723 SkASSERT(key);
2724
2725 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002726 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002727 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002728 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002729 }
Mike Reed38992392019-07-30 10:48:15 -04002730 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002731}
2732
Michael Ludwiga595f862019-08-27 15:25:49 -04002733void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2734 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002735 SkASSERT(r.isSorted());
2736
2737 // If this used a paint, it would be a filled color with blend mode, which does not
2738 // need to use an autodraw loop, so use SkDrawIter directly.
2739 if (this->quickReject(r)) {
2740 return;
2741 }
2742
Michael Ludwiga4b44882019-08-28 14:34:58 -04002743 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002744 SkDrawIter iter(this);
2745 while(iter.next()) {
2746 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2747 }
2748}
2749
2750void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2751 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2752 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002753 if (count <= 0) {
2754 // Nothing to draw
2755 return;
2756 }
2757
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002758 SkPaint realPaint;
2759 init_image_paint(&realPaint, paint);
2760
Michael Ludwiga4b44882019-08-28 14:34:58 -04002761 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2762 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2763 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2764 // or we need it for the autolooper (since it greatly improves image filter perf).
2765 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2766 bool setBoundsValid = count == 1 || needsAutoLooper;
2767 SkRect setBounds = imageSet[0].fDstRect;
2768 if (imageSet[0].fMatrixIndex >= 0) {
2769 // Account for the per-entry transform that is applied prior to the CTM when drawing
2770 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002771 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002772 if (needsAutoLooper) {
2773 for (int i = 1; i < count; ++i) {
2774 SkRect entryBounds = imageSet[i].fDstRect;
2775 if (imageSet[i].fMatrixIndex >= 0) {
2776 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2777 }
2778 setBounds.joinPossiblyEmptyRect(entryBounds);
2779 }
2780 }
2781
2782 // If we happen to have the draw bounds, though, might as well check quickReject().
2783 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2784 SkRect tmp;
2785 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2786 return;
2787 }
2788 }
2789
2790 if (needsAutoLooper) {
2791 SkASSERT(setBoundsValid);
2792 DRAW_BEGIN(realPaint, &setBounds)
2793 while (iter.next()) {
2794 iter.fDevice->drawEdgeAAImageSet(
2795 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2796 }
2797 DRAW_END
2798 } else {
2799 this->predrawNotify();
2800 SkDrawIter iter(this);
2801 while(iter.next()) {
2802 iter.fDevice->drawEdgeAAImageSet(
2803 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2804 }
2805 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002806}
2807
reed@android.com8a1c16f2008-12-17 15:59:43 +00002808//////////////////////////////////////////////////////////////////////////////
2809// These methods are NOT virtual, and therefore must call back into virtual
2810// methods, rather than actually drawing themselves.
2811//////////////////////////////////////////////////////////////////////////////
2812
reed374772b2016-10-05 17:33:02 -07002813void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002814 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002815 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002816 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002817 this->drawPaint(paint);
2818}
2819
2820void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002821 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002822 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2823}
2824
Mike Reed3661bc92017-02-22 13:21:42 -05002825void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002826 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002827 pts[0].set(x0, y0);
2828 pts[1].set(x1, y1);
2829 this->drawPoints(kLines_PointMode, 2, pts, paint);
2830}
2831
Mike Reed3661bc92017-02-22 13:21:42 -05002832void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002833 if (radius < 0) {
2834 radius = 0;
2835 }
2836
2837 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002838 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002839 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002840}
2841
2842void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2843 const SkPaint& paint) {
2844 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002845 SkRRect rrect;
2846 rrect.setRectXY(r, rx, ry);
2847 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848 } else {
2849 this->drawRect(r, paint);
2850 }
2851}
2852
reed@android.com8a1c16f2008-12-17 15:59:43 +00002853void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2854 SkScalar sweepAngle, bool useCenter,
2855 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002856 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002857 if (oval.isEmpty() || !sweepAngle) {
2858 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002859 }
bsalomon21af9ca2016-08-25 12:29:23 -07002860 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002861}
2862
reed@android.comf76bacf2009-05-13 14:00:33 +00002863///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002864#ifdef SK_DISABLE_SKPICTURE
2865void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002866
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002867
2868void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2869 const SkPaint* paint) {}
2870#else
Mike Klein88d90712018-01-27 17:30:04 +00002871/**
2872 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2873 * against the playback cost of recursing into the subpicture to get at its actual ops.
2874 *
2875 * For now we pick a conservatively small value, though measurement (and other heuristics like
2876 * the type of ops contained) may justify changing this value.
2877 */
2878#define kMaxPictureOpsToUnrollInsteadOfRef 1
2879
reedd5fa1a42014-08-09 11:08:05 -07002880void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002881 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002882 RETURN_ON_NULL(picture);
2883
reede3b38ce2016-01-08 09:18:44 -08002884 if (matrix && matrix->isIdentity()) {
2885 matrix = nullptr;
2886 }
Mike Klein88d90712018-01-27 17:30:04 +00002887 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2888 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2889 picture->playback(this);
2890 } else {
2891 this->onDrawPicture(picture, matrix, paint);
2892 }
reedd5fa1a42014-08-09 11:08:05 -07002893}
robertphillips9b14f262014-06-04 05:40:44 -07002894
reedd5fa1a42014-08-09 11:08:05 -07002895void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2896 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002897 if (!paint || paint->canComputeFastBounds()) {
2898 SkRect bounds = picture->cullRect();
2899 if (paint) {
2900 paint->computeFastBounds(bounds, &bounds);
2901 }
2902 if (matrix) {
2903 matrix->mapRect(&bounds);
2904 }
2905 if (this->quickReject(bounds)) {
2906 return;
2907 }
2908 }
2909
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002910 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002911 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002912}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002913#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002914
reed@android.com8a1c16f2008-12-17 15:59:43 +00002915///////////////////////////////////////////////////////////////////////////////
2916///////////////////////////////////////////////////////////////////////////////
2917
reed3aafe112016-08-18 12:45:34 -07002918SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002919 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920
2921 SkASSERT(canvas);
2922
reed3aafe112016-08-18 12:45:34 -07002923 fImpl = new (fStorage) SkDrawIter(canvas);
Michael Ludwig2a8a3ff2020-03-26 15:33:20 -04002924 // This advances the base iterator to the first device and caches its origin,
2925 // correctly handling the case where there are no devices.
2926 this->next();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927}
2928
2929SkCanvas::LayerIter::~LayerIter() {
2930 fImpl->~SkDrawIter();
2931}
2932
2933void SkCanvas::LayerIter::next() {
2934 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002935 if (!fDone) {
2936 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2937 // filters, so its devices will always be able to report the origin exactly.
2938 fDeviceOrigin = fImpl->fDevice->getOrigin();
2939 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940}
2941
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002942SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002943 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944}
2945
2946const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002947 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002948}
2949
2950const SkPaint& SkCanvas::LayerIter::paint() const {
2951 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002952 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953 paint = &fDefaultPaint;
2954 }
2955 return *paint;
2956}
2957
Mike Reedca37f322018-03-08 13:22:16 -05002958SkIRect SkCanvas::LayerIter::clipBounds() const {
2959 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002960}
2961
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002962int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2963int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002964
2965///////////////////////////////////////////////////////////////////////////////
2966
Brian Osmane8a98632019-04-10 10:26:10 -04002967SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2968SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2969SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2970SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
2971
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002972SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2973 const SkRect& dstRect, int matrixIndex, float alpha,
2974 unsigned aaFlags, bool hasClip)
2975 : fImage(std::move(image))
2976 , fSrcRect(srcRect)
2977 , fDstRect(dstRect)
2978 , fMatrixIndex(matrixIndex)
2979 , fAlpha(alpha)
2980 , fAAFlags(aaFlags)
2981 , fHasClip(hasClip) {}
2982
2983SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2984 const SkRect& dstRect, float alpha, unsigned aaFlags)
2985 : fImage(std::move(image))
2986 , fSrcRect(srcRect)
2987 , fDstRect(dstRect)
2988 , fAlpha(alpha)
2989 , fAAFlags(aaFlags) {}
2990
2991///////////////////////////////////////////////////////////////////////////////
2992
Mike Reed5df49342016-11-12 08:06:55 -06002993std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05002994 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04002995 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002996 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002997 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002998
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002999 SkBitmap bitmap;
3000 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003001 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003002 }
Mike Reed12f77342017-11-08 11:19:52 -05003003
3004 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003005 std::make_unique<SkCanvas>(bitmap, *props) :
3006 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003007}
reedd5fa1a42014-08-09 11:08:05 -07003008
3009///////////////////////////////////////////////////////////////////////////////
3010
Florin Malitaee424ac2016-12-01 12:47:59 -05003011SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003012 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003013
Florin Malita439ace92016-12-02 12:05:41 -05003014SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003015 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003016
Herb Derbyefe39bc2018-05-01 17:06:20 -04003017SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003018 : INHERITED(device) {}
3019
Florin Malitaee424ac2016-12-01 12:47:59 -05003020SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3021 (void)this->INHERITED::getSaveLayerStrategy(rec);
3022 return kNoLayer_SaveLayerStrategy;
3023}
3024
Mike Reed148b7fd2018-12-18 17:38:18 -05003025bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3026 return false;
3027}
3028
Florin Malitaee424ac2016-12-01 12:47:59 -05003029///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003030
reed73603f32016-09-20 08:42:38 -07003031static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3032static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3033static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3034static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3035static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3036static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003037
3038///////////////////////////////////////////////////////////////////////////////////////////////////
3039
3040SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3041 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003042 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003043 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003044 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003045 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003046 clip.setEmpty();
3047 }
3048
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003049 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003050 return handle;
3051 }
3052 return nullptr;
3053}
3054
3055static bool install(SkBitmap* bm, const SkImageInfo& info,
3056 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003057 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003058}
3059
3060SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3061 SkBitmap* bm) {
3062 SkRasterHandleAllocator::Rec rec;
3063 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3064 return nullptr;
3065 }
3066 return rec.fHandle;
3067}
3068
3069std::unique_ptr<SkCanvas>
3070SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3071 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003072 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003073 return nullptr;
3074 }
3075
3076 SkBitmap bm;
3077 Handle hndl;
3078
3079 if (rec) {
3080 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3081 } else {
3082 hndl = alloc->allocBitmap(info, &bm);
3083 }
3084 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3085}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003086
3087///////////////////////////////////////////////////////////////////////////////////////////////////