blob: 15751ab8105e131feab397efd3a343dab1dce0de [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
1956 // If the vertices contain custom attributes, ensure they line up with the paint's shader
1957 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()) {
1965 if (vertices->priv().attributes()[attrIndex++].channelCount() != v.fWidth) {
1966 return;
1967 }
1968 }
1969 }
Brian Osmanf11e3312020-03-24 14:57:38 -04001970
Mike Reed5caf9352020-03-02 14:57:09 -05001971 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001972}
1973
1974void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001975 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001976 this->onDrawPath(path, paint);
1977}
1978
reeda85d4d02015-05-06 12:56:48 -07001979void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001980 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001981 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001982 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001983}
1984
Mike Reedc4e31092018-01-30 11:15:27 -05001985// Returns true if the rect can be "filled" : non-empty and finite
1986static bool fillable(const SkRect& r) {
1987 SkScalar w = r.width();
1988 SkScalar h = r.height();
1989 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1990}
1991
reede47829b2015-08-06 10:02:53 -07001992void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1993 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001994 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001995 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001996 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001997 return;
1998 }
1999 this->onDrawImageRect(image, &src, dst, paint, constraint);
2000}
reed41af9662015-01-05 07:49:08 -08002001
reed84984ef2015-07-17 07:09:43 -07002002void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2003 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002004 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002005 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002006}
2007
Brian Salomonf08002c2018-10-26 16:15:46 -04002008void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002009 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002010 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002011 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002012}
reede47829b2015-08-06 10:02:53 -07002013
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002014namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002015class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002016public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002017 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2018 if (!origPaint) {
2019 return;
2020 }
2021 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2022 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2023 }
2024 if (origPaint->getMaskFilter()) {
2025 fPaint.writable()->setMaskFilter(nullptr);
2026 }
2027 if (origPaint->isAntiAlias()) {
2028 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002029 }
2030 }
2031
2032 const SkPaint* get() const {
2033 return fPaint;
2034 }
2035
2036private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002037 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002038};
2039} // namespace
2040
reed4c21dc52015-06-25 12:32:03 -07002041void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2042 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002043 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002044 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002045 if (dst.isEmpty()) {
2046 return;
2047 }
msarett552bca92016-08-03 06:53:26 -07002048 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002049 LatticePaint latticePaint(paint);
2050 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002051 } else {
reede47829b2015-08-06 10:02:53 -07002052 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002053 }
reed4c21dc52015-06-25 12:32:03 -07002054}
2055
msarett16882062016-08-16 09:31:08 -07002056void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2057 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002058 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002059 RETURN_ON_NULL(image);
2060 if (dst.isEmpty()) {
2061 return;
2062 }
msarett71df2d72016-09-30 12:41:42 -07002063
2064 SkIRect bounds;
2065 Lattice latticePlusBounds = lattice;
2066 if (!latticePlusBounds.fBounds) {
2067 bounds = SkIRect::MakeWH(image->width(), image->height());
2068 latticePlusBounds.fBounds = &bounds;
2069 }
2070
2071 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002072 LatticePaint latticePaint(paint);
2073 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002074 } else {
2075 this->drawImageRect(image, dst, paint);
2076 }
2077}
2078
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002079static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002080 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002081 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002082 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002083 return SkImage::MakeFromBitmap(bitmap);
2084}
2085
2086void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2087 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002088}
2089
reede47829b2015-08-06 10:02:53 -07002090void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002091 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002092 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002093}
2094
reed84984ef2015-07-17 07:09:43 -07002095void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2096 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002097 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002098}
2099
reede47829b2015-08-06 10:02:53 -07002100void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2101 SrcRectConstraint constraint) {
2102 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2103 constraint);
2104}
reede47829b2015-08-06 10:02:53 -07002105
reed71c3c762015-06-24 10:29:17 -07002106void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002107 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002108 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002109 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002110 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002111 if (count <= 0) {
2112 return;
2113 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002114 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002115 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002116 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002117}
2118
reedf70b5312016-03-04 16:36:20 -08002119void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002120 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002121 if (key) {
2122 this->onDrawAnnotation(rect, key, value);
2123 }
2124}
2125
reede47829b2015-08-06 10:02:53 -07002126void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2127 const SkPaint* paint, SrcRectConstraint constraint) {
2128 if (src) {
2129 this->drawImageRect(image, *src, dst, paint, constraint);
2130 } else {
2131 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2132 dst, paint, constraint);
2133 }
2134}
reede47829b2015-08-06 10:02:53 -07002135
Mike Reed4204da22017-05-17 08:53:36 -04002136void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002137 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002138 this->onDrawShadowRec(path, rec);
2139}
2140
2141void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2142 SkPaint paint;
2143 const SkRect& pathBounds = path.getBounds();
2144
Mike Reed38992392019-07-30 10:48:15 -04002145 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002146 while (iter.next()) {
2147 iter.fDevice->drawShadow(path, rec);
2148 }
Mike Reed38992392019-07-30 10:48:15 -04002149 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002150}
2151
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002152void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002153 QuadAAFlags aaFlags, const SkColor4f& color,
2154 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002155 TRACE_EVENT0("skia", TRACE_FUNC);
2156 // Make sure the rect is sorted before passing it along
2157 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2158}
2159
2160void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2161 const SkPoint dstClips[],
2162 const SkMatrix preViewMatrices[],
2163 const SkPaint* paint,
2164 SrcRectConstraint constraint) {
2165 TRACE_EVENT0("skia", TRACE_FUNC);
2166 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2167}
2168
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169//////////////////////////////////////////////////////////////////////////////
2170// These are the virtual drawing methods
2171//////////////////////////////////////////////////////////////////////////////
2172
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002173void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002174 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002175 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2176 }
2177}
2178
reed41af9662015-01-05 07:49:08 -08002179void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002180 this->internalDrawPaint(paint);
2181}
2182
2183void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002184 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185
2186 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002187 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 }
2189
Mike Reed38992392019-07-30 10:48:15 -04002190 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191}
2192
reed41af9662015-01-05 07:49:08 -08002193void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2194 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195 if ((long)count <= 0) {
2196 return;
2197 }
2198
Mike Reed822128b2017-02-28 16:41:03 -05002199 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002200 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002201 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002202 // special-case 2 points (common for drawing a single line)
2203 if (2 == count) {
2204 r.set(pts[0], pts[1]);
2205 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002206 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002207 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002208 if (!r.isFinite()) {
2209 return;
2210 }
Mike Reed822128b2017-02-28 16:41:03 -05002211 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002212 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2213 return;
2214 }
2215 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002216 }
reed@google.coma584aed2012-05-16 14:06:02 +00002217
halcanary96fcdcc2015-08-27 07:41:13 -07002218 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219
Mike Reed38992392019-07-30 10:48:15 -04002220 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002221
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002223 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224 }
reed@google.com4b226022011-01-11 18:32:13 +00002225
Mike Reed38992392019-07-30 10:48:15 -04002226 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227}
2228
reed4a167172016-08-18 17:15:25 -07002229static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002230 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002231}
2232
reed41af9662015-01-05 07:49:08 -08002233void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002234 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002236 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002237 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002238 return;
2239 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240 }
reed@google.com4b226022011-01-11 18:32:13 +00002241
reed4a167172016-08-18 17:15:25 -07002242 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002243 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244
reed4a167172016-08-18 17:15:25 -07002245 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002246 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002247 }
2248
Mike Reed38992392019-07-30 10:48:15 -04002249 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002250 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002251 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002252 SkDrawIter iter(this);
2253 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002254 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002255 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257}
2258
msarett44df6512016-08-25 13:54:30 -07002259void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002260 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002261 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002262 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002263 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2264 return;
2265 }
msarett44df6512016-08-25 13:54:30 -07002266 }
2267
Mike Reed38992392019-07-30 10:48:15 -04002268 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002269
2270 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002271 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002272 }
2273
Mike Reed38992392019-07-30 10:48:15 -04002274 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002275}
2276
Mike Reedd5674082019-04-19 15:00:47 -04002277void SkCanvas::onDrawBehind(const SkPaint& paint) {
2278 SkIRect bounds;
2279 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2280 for (;;) {
2281 const MCRec* rec = (const MCRec*)iter.prev();
2282 if (!rec) {
2283 return; // no backimages, so nothing to draw
2284 }
2285 if (rec->fBackImage) {
2286 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2287 rec->fBackImage->fImage->width(),
2288 rec->fBackImage->fImage->height());
2289 break;
2290 }
2291 }
2292
Mike Reed38992392019-07-30 10:48:15 -04002293 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002294
2295 while (iter.next()) {
2296 SkBaseDevice* dev = iter.fDevice;
2297
Mike Reedd5674082019-04-19 15:00:47 -04002298 dev->save();
2299 // We use clipRegion because it is already defined to operate in dev-space
2300 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2301 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002302 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002303 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002304 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002305 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002306 }
2307
Mike Reed38992392019-07-30 10:48:15 -04002308 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002309}
2310
reed41af9662015-01-05 07:49:08 -08002311void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002312 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002313 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002314 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002315 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002316 return;
2317 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002318 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002319
Mike Reed38992392019-07-30 10:48:15 -04002320 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002321
2322 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002323 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002324 }
2325
Mike Reed38992392019-07-30 10:48:15 -04002326 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002327}
2328
bsalomonac3aa242016-08-19 11:25:19 -07002329void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2330 SkScalar sweepAngle, bool useCenter,
2331 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002332 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002333 if (paint.canComputeFastBounds()) {
2334 SkRect storage;
2335 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002336 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002337 return;
2338 }
bsalomonac3aa242016-08-19 11:25:19 -07002339 }
2340
Mike Reed38992392019-07-30 10:48:15 -04002341 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002342
2343 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002344 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002345 }
2346
Mike Reed38992392019-07-30 10:48:15 -04002347 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002348}
2349
reed41af9662015-01-05 07:49:08 -08002350void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002351 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002352 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002353 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2354 return;
2355 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002356 }
2357
2358 if (rrect.isRect()) {
2359 // call the non-virtual version
2360 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002361 return;
2362 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002363 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002364 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2365 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002366 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002367
Mike Reed38992392019-07-30 10:48:15 -04002368 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002369
2370 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002371 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002372 }
2373
Mike Reed38992392019-07-30 10:48:15 -04002374 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002375}
2376
Mike Reed822128b2017-02-28 16:41:03 -05002377void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002378 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002379 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002380 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2381 return;
2382 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002383 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002384
Mike Reed38992392019-07-30 10:48:15 -04002385 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002386
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002387 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002388 iter.fDevice->drawDRRect(outer, inner, draw.paint());
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_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002392}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002393
reed41af9662015-01-05 07:49:08 -08002394void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002395 if (!path.isFinite()) {
2396 return;
2397 }
2398
Mike Reed822128b2017-02-28 16:41:03 -05002399 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002400 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002401 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002402 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2403 return;
2404 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002406
Mike Reed822128b2017-02-28 16:41:03 -05002407 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002408 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002409 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002410 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002411 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002412 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413
Mike Reed38992392019-07-30 10:48:15 -04002414 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002415
2416 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002417 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002418 }
2419
Mike Reed38992392019-07-30 10:48:15 -04002420 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002421}
2422
reed262a71b2015-12-05 13:07:27 -08002423bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002424 if (!paint.getImageFilter()) {
2425 return false;
2426 }
2427
2428 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002429 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002430 return false;
2431 }
2432
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002433 // The other paint effects need to be applied before the image filter, but the sprite draw
2434 // applies the filter explicitly first.
2435 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2436 return false;
2437 }
reed262a71b2015-12-05 13:07:27 -08002438 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2439 // Once we can filter and the filter will return a result larger than itself, we should be
2440 // able to remove this constraint.
2441 // skbug.com/4526
2442 //
2443 SkPoint pt;
2444 ctm.mapXY(x, y, &pt);
2445 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2446 return ir.contains(fMCRec->fRasterClip.getBounds());
2447}
2448
Mike Reedf441cfc2018-04-11 14:50:16 -04002449// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2450// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2451// null.
2452static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2453 if (paintParam) {
2454 *real = *paintParam;
2455 real->setStyle(SkPaint::kFill_Style);
2456 real->setPathEffect(nullptr);
2457 paintParam = real;
2458 }
2459 return paintParam;
2460}
2461
reeda85d4d02015-05-06 12:56:48 -07002462void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002463 SkPaint realPaint;
2464 paint = init_image_paint(&realPaint, paint);
2465
reeda85d4d02015-05-06 12:56:48 -07002466 SkRect bounds = SkRect::MakeXYWH(x, y,
2467 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002468 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002469 SkRect tmp = bounds;
2470 if (paint) {
2471 paint->computeFastBounds(tmp, &tmp);
2472 }
2473 if (this->quickReject(tmp)) {
2474 return;
2475 }
reeda85d4d02015-05-06 12:56:48 -07002476 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002477 // At this point we need a real paint object. If the caller passed null, then we should
2478 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2479 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2480 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002481
reeda2217ef2016-07-20 06:04:34 -07002482 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002483 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2484 *paint);
2485 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002486 special = this->getDevice()->makeSpecial(image);
2487 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002488 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002489 }
2490 }
2491
Mike Reed38992392019-07-30 10:48:15 -04002492 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002493
reeda85d4d02015-05-06 12:56:48 -07002494 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002495 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002496 if (special) {
2497 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002498 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002499 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002500 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002501 SkScalarRoundToInt(pt.fY), pnt,
2502 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002503 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002504 iter.fDevice->drawImageRect(
2505 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2506 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002507 }
reeda85d4d02015-05-06 12:56:48 -07002508 }
halcanary9d524f22016-03-29 09:03:52 -07002509
Mike Reed38992392019-07-30 10:48:15 -04002510 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002511}
2512
reed41af9662015-01-05 07:49:08 -08002513void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002514 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002515 SkPaint realPaint;
2516 paint = init_image_paint(&realPaint, paint);
2517
halcanary96fcdcc2015-08-27 07:41:13 -07002518 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002519 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002520 if (paint) {
2521 paint->computeFastBounds(dst, &storage);
2522 }
2523 if (this->quickReject(storage)) {
2524 return;
2525 }
reeda85d4d02015-05-06 12:56:48 -07002526 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002527 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002528
Mike Reed38992392019-07-30 10:48:15 -04002529 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002530
reeda85d4d02015-05-06 12:56:48 -07002531 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002532 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002533 }
halcanary9d524f22016-03-29 09:03:52 -07002534
Mike Reed38992392019-07-30 10:48:15 -04002535 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002536}
2537
reed4c21dc52015-06-25 12:32:03 -07002538void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2539 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002540 SkPaint realPaint;
2541 paint = init_image_paint(&realPaint, paint);
2542
halcanary96fcdcc2015-08-27 07:41:13 -07002543 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002544 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002545 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2546 return;
2547 }
reed@google.com3d608122011-11-21 15:16:16 +00002548 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002549 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002550
Mike Reed38992392019-07-30 10:48:15 -04002551 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002552
reed4c21dc52015-06-25 12:32:03 -07002553 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002554 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002555 }
halcanary9d524f22016-03-29 09:03:52 -07002556
Mike Reed38992392019-07-30 10:48:15 -04002557 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002558}
2559
msarett16882062016-08-16 09:31:08 -07002560void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2561 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002562 SkPaint realPaint;
2563 paint = init_image_paint(&realPaint, paint);
2564
msarett16882062016-08-16 09:31:08 -07002565 if (nullptr == paint || paint->canComputeFastBounds()) {
2566 SkRect storage;
2567 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2568 return;
2569 }
2570 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002571 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002572
Mike Reed38992392019-07-30 10:48:15 -04002573 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002574
2575 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002576 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002577 }
2578
Mike Reed38992392019-07-30 10:48:15 -04002579 DRAW_END
msarett16882062016-08-16 09:31:08 -07002580}
2581
fmalita00d5c2c2014-08-21 08:53:26 -07002582void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2583 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002584 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002585 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002586 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002587 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002588 SkRect tmp;
2589 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2590 return;
2591 }
2592 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002593 }
2594
fmalita024f9962015-03-03 19:08:17 -08002595 // We cannot filter in the looper as we normally do, because the paint is
2596 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002597 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002598
fmalitaaa1b9122014-08-28 14:32:24 -07002599 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002600 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002601 }
2602
Mike Reed38992392019-07-30 10:48:15 -04002603 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002604}
2605
Mike Reed358fcad2018-11-23 15:27:51 -05002606// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002607void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002608 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2609 TRACE_EVENT0("skia", TRACE_FUNC);
2610 if (byteLength) {
2611 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002612 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002613 }
2614}
Mike Reed4f81bb72019-01-23 09:23:00 -05002615
fmalita00d5c2c2014-08-21 08:53:26 -07002616void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2617 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002618 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002619 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002620 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002621 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002622}
reed@google.come0d9ce82014-04-23 04:00:17 +00002623
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002624void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2625 const SkPaint& paint) {
2626 DRAW_BEGIN(paint, nullptr)
2627
2628 while (iter.next()) {
2629 // In the common case of one iteration we could std::move vertices here.
2630 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2631 }
2632
2633 DRAW_END
2634}
Brian Salomon199fb872017-02-06 09:41:10 -05002635
dandovb3c9d1c2014-08-12 08:34:29 -07002636void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002637 const SkPoint texCoords[4], SkBlendMode bmode,
2638 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002639 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002640 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002641 return;
2642 }
mtklein6cfa73a2014-08-13 13:33:49 -07002643
Mike Reedfaba3712016-11-03 14:45:31 -04002644 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002645}
2646
2647void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002648 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002649 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002650 // Since a patch is always within the convex hull of the control points, we discard it when its
2651 // bounding rectangle is completely outside the current clip.
2652 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002653 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002654 if (this->quickReject(bounds)) {
2655 return;
2656 }
mtklein6cfa73a2014-08-13 13:33:49 -07002657
Mike Reed38992392019-07-30 10:48:15 -04002658 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002659
dandovecfff212014-08-04 10:02:00 -07002660 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002661 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002662 }
mtklein6cfa73a2014-08-13 13:33:49 -07002663
Mike Reed38992392019-07-30 10:48:15 -04002664 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002665}
2666
reeda8db7282015-07-07 10:22:31 -07002667void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002668#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002669 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002670#endif
reede3b38ce2016-01-08 09:18:44 -08002671 RETURN_ON_NULL(dr);
2672 if (x || y) {
2673 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2674 this->onDrawDrawable(dr, &matrix);
2675 } else {
2676 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002677 }
2678}
2679
reeda8db7282015-07-07 10:22:31 -07002680void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002681#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002682 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002683#endif
reede3b38ce2016-01-08 09:18:44 -08002684 RETURN_ON_NULL(dr);
2685 if (matrix && matrix->isIdentity()) {
2686 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002687 }
reede3b38ce2016-01-08 09:18:44 -08002688 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002689}
2690
2691void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002692 // drawable bounds are no longer reliable (e.g. android displaylist)
2693 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002694 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002695}
2696
reed71c3c762015-06-24 10:29:17 -07002697void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002698 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002699 const SkRect* cull, const SkPaint* paint) {
2700 if (cull && this->quickReject(*cull)) {
2701 return;
2702 }
2703
2704 SkPaint pnt;
2705 if (paint) {
2706 pnt = *paint;
2707 }
halcanary9d524f22016-03-29 09:03:52 -07002708
Mike Reed38992392019-07-30 10:48:15 -04002709 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002710 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002711 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002712 }
Mike Reed38992392019-07-30 10:48:15 -04002713 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002714}
2715
reedf70b5312016-03-04 16:36:20 -08002716void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2717 SkASSERT(key);
2718
2719 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002720 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002721 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002722 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002723 }
Mike Reed38992392019-07-30 10:48:15 -04002724 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002725}
2726
Michael Ludwiga595f862019-08-27 15:25:49 -04002727void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2728 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002729 SkASSERT(r.isSorted());
2730
2731 // If this used a paint, it would be a filled color with blend mode, which does not
2732 // need to use an autodraw loop, so use SkDrawIter directly.
2733 if (this->quickReject(r)) {
2734 return;
2735 }
2736
Michael Ludwiga4b44882019-08-28 14:34:58 -04002737 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002738 SkDrawIter iter(this);
2739 while(iter.next()) {
2740 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2741 }
2742}
2743
2744void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2745 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2746 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002747 if (count <= 0) {
2748 // Nothing to draw
2749 return;
2750 }
2751
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002752 SkPaint realPaint;
2753 init_image_paint(&realPaint, paint);
2754
Michael Ludwiga4b44882019-08-28 14:34:58 -04002755 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2756 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2757 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2758 // or we need it for the autolooper (since it greatly improves image filter perf).
2759 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2760 bool setBoundsValid = count == 1 || needsAutoLooper;
2761 SkRect setBounds = imageSet[0].fDstRect;
2762 if (imageSet[0].fMatrixIndex >= 0) {
2763 // Account for the per-entry transform that is applied prior to the CTM when drawing
2764 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002765 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002766 if (needsAutoLooper) {
2767 for (int i = 1; i < count; ++i) {
2768 SkRect entryBounds = imageSet[i].fDstRect;
2769 if (imageSet[i].fMatrixIndex >= 0) {
2770 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2771 }
2772 setBounds.joinPossiblyEmptyRect(entryBounds);
2773 }
2774 }
2775
2776 // If we happen to have the draw bounds, though, might as well check quickReject().
2777 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2778 SkRect tmp;
2779 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2780 return;
2781 }
2782 }
2783
2784 if (needsAutoLooper) {
2785 SkASSERT(setBoundsValid);
2786 DRAW_BEGIN(realPaint, &setBounds)
2787 while (iter.next()) {
2788 iter.fDevice->drawEdgeAAImageSet(
2789 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2790 }
2791 DRAW_END
2792 } else {
2793 this->predrawNotify();
2794 SkDrawIter iter(this);
2795 while(iter.next()) {
2796 iter.fDevice->drawEdgeAAImageSet(
2797 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2798 }
2799 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002800}
2801
reed@android.com8a1c16f2008-12-17 15:59:43 +00002802//////////////////////////////////////////////////////////////////////////////
2803// These methods are NOT virtual, and therefore must call back into virtual
2804// methods, rather than actually drawing themselves.
2805//////////////////////////////////////////////////////////////////////////////
2806
reed374772b2016-10-05 17:33:02 -07002807void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002808 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002809 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002810 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002811 this->drawPaint(paint);
2812}
2813
2814void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002815 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002816 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2817}
2818
Mike Reed3661bc92017-02-22 13:21:42 -05002819void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002820 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002821 pts[0].set(x0, y0);
2822 pts[1].set(x1, y1);
2823 this->drawPoints(kLines_PointMode, 2, pts, paint);
2824}
2825
Mike Reed3661bc92017-02-22 13:21:42 -05002826void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002827 if (radius < 0) {
2828 radius = 0;
2829 }
2830
2831 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002832 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002833 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002834}
2835
2836void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2837 const SkPaint& paint) {
2838 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002839 SkRRect rrect;
2840 rrect.setRectXY(r, rx, ry);
2841 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002842 } else {
2843 this->drawRect(r, paint);
2844 }
2845}
2846
reed@android.com8a1c16f2008-12-17 15:59:43 +00002847void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2848 SkScalar sweepAngle, bool useCenter,
2849 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002850 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002851 if (oval.isEmpty() || !sweepAngle) {
2852 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002853 }
bsalomon21af9ca2016-08-25 12:29:23 -07002854 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002855}
2856
reed@android.comf76bacf2009-05-13 14:00:33 +00002857///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002858#ifdef SK_DISABLE_SKPICTURE
2859void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002860
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002861
2862void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2863 const SkPaint* paint) {}
2864#else
Mike Klein88d90712018-01-27 17:30:04 +00002865/**
2866 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2867 * against the playback cost of recursing into the subpicture to get at its actual ops.
2868 *
2869 * For now we pick a conservatively small value, though measurement (and other heuristics like
2870 * the type of ops contained) may justify changing this value.
2871 */
2872#define kMaxPictureOpsToUnrollInsteadOfRef 1
2873
reedd5fa1a42014-08-09 11:08:05 -07002874void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002875 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002876 RETURN_ON_NULL(picture);
2877
reede3b38ce2016-01-08 09:18:44 -08002878 if (matrix && matrix->isIdentity()) {
2879 matrix = nullptr;
2880 }
Mike Klein88d90712018-01-27 17:30:04 +00002881 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2882 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2883 picture->playback(this);
2884 } else {
2885 this->onDrawPicture(picture, matrix, paint);
2886 }
reedd5fa1a42014-08-09 11:08:05 -07002887}
robertphillips9b14f262014-06-04 05:40:44 -07002888
reedd5fa1a42014-08-09 11:08:05 -07002889void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2890 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002891 if (!paint || paint->canComputeFastBounds()) {
2892 SkRect bounds = picture->cullRect();
2893 if (paint) {
2894 paint->computeFastBounds(bounds, &bounds);
2895 }
2896 if (matrix) {
2897 matrix->mapRect(&bounds);
2898 }
2899 if (this->quickReject(bounds)) {
2900 return;
2901 }
2902 }
2903
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002904 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002905 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002906}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002907#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002908
reed@android.com8a1c16f2008-12-17 15:59:43 +00002909///////////////////////////////////////////////////////////////////////////////
2910///////////////////////////////////////////////////////////////////////////////
2911
reed3aafe112016-08-18 12:45:34 -07002912SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002913 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002914
2915 SkASSERT(canvas);
2916
reed3aafe112016-08-18 12:45:34 -07002917 fImpl = new (fStorage) SkDrawIter(canvas);
Michael Ludwig2a8a3ff2020-03-26 15:33:20 -04002918 // This advances the base iterator to the first device and caches its origin,
2919 // correctly handling the case where there are no devices.
2920 this->next();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002921}
2922
2923SkCanvas::LayerIter::~LayerIter() {
2924 fImpl->~SkDrawIter();
2925}
2926
2927void SkCanvas::LayerIter::next() {
2928 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002929 if (!fDone) {
2930 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2931 // filters, so its devices will always be able to report the origin exactly.
2932 fDeviceOrigin = fImpl->fDevice->getOrigin();
2933 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934}
2935
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002936SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002937 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002938}
2939
2940const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002941 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002942}
2943
2944const SkPaint& SkCanvas::LayerIter::paint() const {
2945 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002946 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002947 paint = &fDefaultPaint;
2948 }
2949 return *paint;
2950}
2951
Mike Reedca37f322018-03-08 13:22:16 -05002952SkIRect SkCanvas::LayerIter::clipBounds() const {
2953 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002954}
2955
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002956int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2957int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002958
2959///////////////////////////////////////////////////////////////////////////////
2960
Brian Osmane8a98632019-04-10 10:26:10 -04002961SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2962SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2963SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2964SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
2965
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002966SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2967 const SkRect& dstRect, int matrixIndex, float alpha,
2968 unsigned aaFlags, bool hasClip)
2969 : fImage(std::move(image))
2970 , fSrcRect(srcRect)
2971 , fDstRect(dstRect)
2972 , fMatrixIndex(matrixIndex)
2973 , fAlpha(alpha)
2974 , fAAFlags(aaFlags)
2975 , fHasClip(hasClip) {}
2976
2977SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2978 const SkRect& dstRect, float alpha, unsigned aaFlags)
2979 : fImage(std::move(image))
2980 , fSrcRect(srcRect)
2981 , fDstRect(dstRect)
2982 , fAlpha(alpha)
2983 , fAAFlags(aaFlags) {}
2984
2985///////////////////////////////////////////////////////////////////////////////
2986
Mike Reed5df49342016-11-12 08:06:55 -06002987std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05002988 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04002989 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002990 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002991 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002992
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002993 SkBitmap bitmap;
2994 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002995 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002996 }
Mike Reed12f77342017-11-08 11:19:52 -05002997
2998 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05002999 std::make_unique<SkCanvas>(bitmap, *props) :
3000 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003001}
reedd5fa1a42014-08-09 11:08:05 -07003002
3003///////////////////////////////////////////////////////////////////////////////
3004
Florin Malitaee424ac2016-12-01 12:47:59 -05003005SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003006 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003007
Florin Malita439ace92016-12-02 12:05:41 -05003008SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003009 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003010
Herb Derbyefe39bc2018-05-01 17:06:20 -04003011SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003012 : INHERITED(device) {}
3013
Florin Malitaee424ac2016-12-01 12:47:59 -05003014SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3015 (void)this->INHERITED::getSaveLayerStrategy(rec);
3016 return kNoLayer_SaveLayerStrategy;
3017}
3018
Mike Reed148b7fd2018-12-18 17:38:18 -05003019bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3020 return false;
3021}
3022
Florin Malitaee424ac2016-12-01 12:47:59 -05003023///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003024
reed73603f32016-09-20 08:42:38 -07003025static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3026static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3027static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3028static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3029static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3030static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003031
3032///////////////////////////////////////////////////////////////////////////////////////////////////
3033
3034SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3035 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003036 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003037 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003038 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003039 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003040 clip.setEmpty();
3041 }
3042
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003043 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003044 return handle;
3045 }
3046 return nullptr;
3047}
3048
3049static bool install(SkBitmap* bm, const SkImageInfo& info,
3050 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003051 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003052}
3053
3054SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3055 SkBitmap* bm) {
3056 SkRasterHandleAllocator::Rec rec;
3057 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3058 return nullptr;
3059 }
3060 return rec.fHandle;
3061}
3062
3063std::unique_ptr<SkCanvas>
3064SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3065 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003066 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003067 return nullptr;
3068 }
3069
3070 SkBitmap bm;
3071 Handle hndl;
3072
3073 if (rec) {
3074 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3075 } else {
3076 hndl = alloc->allocBitmap(info, &bm);
3077 }
3078 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3079}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003080
3081///////////////////////////////////////////////////////////////////////////////////////////////////