blob: 01ca4b0d7a79d29c2ee785169b4451aa52294d2f [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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/private/SkNx.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040023#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050025#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050035#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050036#include "src/core/SkMatrixUtils.h"
37#include "src/core/SkPaintPriv.h"
38#include "src/core/SkRasterClip.h"
39#include "src/core/SkSpecialImage.h"
40#include "src/core/SkStrikeCache.h"
41#include "src/core/SkTLazy.h"
42#include "src/core/SkTextFormatParams.h"
43#include "src/core/SkTraceEvent.h"
Mike Reedba962562020-03-12 20:33:21 -040044#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050045#include "src/image/SkImage_Base.h"
46#include "src/image/SkSurface_Base.h"
47#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040048
bungemand3ebb482015-08-05 13:57:49 -070049#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000050
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000051#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050052#include "include/gpu/GrContext.h"
53#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000054#endif
55
reede3b38ce2016-01-08 09:18:44 -080056#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050057#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080058
Mike Klein1bb7e232019-12-10 08:58:52 -060059// This is a test: static_assert with no message is a c++17 feature,
60// and std::max() is constexpr only since the c++14 stdlib.
61static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000062
Mike Reed139e5e02017-03-08 11:29:33 -050063///////////////////////////////////////////////////////////////////////////////////////////////////
64
reedc83a2972015-07-16 07:40:45 -070065/*
66 * Return true if the drawing this rect would hit every pixels in the canvas.
67 *
68 * Returns false if
69 * - rect does not contain the canvas' bounds
70 * - paint is not fill
71 * - paint would blur or otherwise change the coverage of the rect
72 */
73bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
74 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070075 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
76 (int)kNone_ShaderOverrideOpacity,
77 "need_matching_enums0");
78 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
79 (int)kOpaque_ShaderOverrideOpacity,
80 "need_matching_enums1");
81 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
82 (int)kNotOpaque_ShaderOverrideOpacity,
83 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070084
85 const SkISize size = this->getBaseLayerSize();
86 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050087
88 // if we're clipped at all, we can't overwrite the entire surface
89 {
90 SkBaseDevice* base = this->getDevice();
91 SkBaseDevice* top = this->getTopDevice();
92 if (base != top) {
93 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
94 }
95 if (!base->clipIsWideOpen()) {
96 return false;
97 }
reedc83a2972015-07-16 07:40:45 -070098 }
99
100 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700101 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700102 return false; // conservative
103 }
halcanaryc5769b22016-08-10 07:13:21 -0700104
105 SkRect devRect;
106 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
107 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700108 return false;
109 }
110 }
111
112 if (paint) {
113 SkPaint::Style paintStyle = paint->getStyle();
114 if (!(paintStyle == SkPaint::kFill_Style ||
115 paintStyle == SkPaint::kStrokeAndFill_Style)) {
116 return false;
117 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400118 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700119 return false; // conservative
120 }
121 }
122 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
123}
124
125///////////////////////////////////////////////////////////////////////////////////////////////////
126
reed@google.comda17f752012-08-16 18:27:05 +0000127// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128//#define SK_TRACE_SAVERESTORE
129
130#ifdef SK_TRACE_SAVERESTORE
131 static int gLayerCounter;
132 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
133 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
134
135 static int gRecCounter;
136 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
137 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
138
139 static int gCanvasCounter;
140 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
141 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
142#else
143 #define inc_layer()
144 #define dec_layer()
145 #define inc_rec()
146 #define dec_rec()
147 #define inc_canvas()
148 #define dec_canvas()
149#endif
150
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000151typedef SkTLazy<SkPaint> SkLazyPaint;
152
reedc83a2972015-07-16 07:40:45 -0700153void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000154 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700155 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
156 ? SkSurface::kDiscard_ContentChangeMode
157 : SkSurface::kRetain_ContentChangeMode);
158 }
159}
160
161void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
162 ShaderOverrideOpacity overrideOpacity) {
163 if (fSurfaceBase) {
164 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
165 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
166 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
167 // and therefore we don't care which mode we're in.
168 //
169 if (fSurfaceBase->outstandingImageSnapshot()) {
170 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
171 mode = SkSurface::kDiscard_ContentChangeMode;
172 }
173 }
174 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000175 }
176}
177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000180/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 The clip/matrix/proc are fields that reflect the top of the save/restore
182 stack. Whenever the canvas changes, it marks a dirty flag, and then before
183 these are used (assuming we're not on a layer) we rebuild these cache
184 values: they reflect the top of the save stack, but translated and clipped
185 by the device's XY offset and bitmap-bounds.
186*/
187struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400188 DeviceCM* fNext;
189 sk_sp<SkBaseDevice> fDevice;
190 SkRasterClip fClip;
191 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
192 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400193 sk_sp<SkImage> fClipImage;
194 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195
Florin Malita53f77bd2017-04-28 13:48:37 -0400196 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000197 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700198 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400199 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500200 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700201 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000202 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400203 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400204 {}
reed@google.com4b226022011-01-11 18:32:13 +0000205
mtkleinfeaadee2015-04-08 11:25:48 -0700206 void reset(const SkIRect& bounds) {
207 SkASSERT(!fPaint);
208 SkASSERT(!fNext);
209 SkASSERT(fDevice);
210 fClip.setRect(bounds);
211 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212};
213
Mike Reed148b7fd2018-12-18 17:38:18 -0500214namespace {
215// Encapsulate state needed to restore from saveBehind()
216struct BackImage {
217 sk_sp<SkSpecialImage> fImage;
218 SkIPoint fLoc;
219};
220}
221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222/* This is the record we keep for each save/restore level in the stack.
223 Since a level optionally copies the matrix and/or stack, we have pointers
224 for these fields. If the value is copied for this level, the copy is
225 stored in the ...Storage field, and the pointer points to that. If the
226 value is not copied for this level, we ignore ...Storage, and just point
227 at the corresponding value in the previous level in the stack.
228*/
229class SkCanvas::MCRec {
230public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500231 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 /* If there are any layers in the stack, this points to the top-most
233 one that is at or below this level in the stack (so we know what
234 bitmap/device to draw into from this level. This value is NOT
235 reference counted, since the real owner is either our fLayer field,
236 or a previous one in a lower level.)
237 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500238 DeviceCM* fTopLayer;
239 std::unique_ptr<BackImage> fBackImage;
240 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500241 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500242 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
Mike Reeda1361362017-03-07 09:37:29 -0500244 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700245 fLayer = nullptr;
246 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800247 fMatrix.reset();
248 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700249
reedd9544982014-09-09 18:46:22 -0700250 // don't bother initializing fNext
251 inc_rec();
252 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400253 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700254 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700255 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800256 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 // don't bother initializing fNext
259 inc_rec();
260 }
261 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700262 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 dec_rec();
264 }
mtkleinfeaadee2015-04-08 11:25:48 -0700265
266 void reset(const SkIRect& bounds) {
267 SkASSERT(fLayer);
268 SkASSERT(fDeferredSaveCount == 0);
269
270 fMatrix.reset();
271 fRasterClip.setRect(bounds);
272 fLayer->reset(bounds);
273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274};
275
Mike Reeda1361362017-03-07 09:37:29 -0500276class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277public:
Mike Reeda1361362017-03-07 09:37:29 -0500278 SkDrawIter(SkCanvas* canvas)
279 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
280 {}
reed@google.com4b226022011-01-11 18:32:13 +0000281
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000283 const DeviceCM* rec = fCurrLayer;
284 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400285 fDevice = rec->fDevice.get();
286 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700288 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 return true;
290 }
291 return false;
292 }
reed@google.com4b226022011-01-11 18:32:13 +0000293
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000295
Mike Reed99330ba2017-02-22 11:01:08 -0500296 SkBaseDevice* fDevice;
297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 const DeviceCM* fCurrLayer;
300 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301};
302
Florin Malita713b8ef2017-04-28 10:57:24 -0400303#define FOR_EACH_TOP_DEVICE( code ) \
304 do { \
305 DeviceCM* layer = fMCRec->fTopLayer; \
306 while (layer) { \
307 SkBaseDevice* device = layer->fDevice.get(); \
308 if (device) { \
309 code; \
310 } \
311 layer = layer->fNext; \
312 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500313 } while (0)
314
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315/////////////////////////////////////////////////////////////////////////////
316
reeddbc3cef2015-04-29 12:18:57 -0700317/**
318 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700319 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700320 */
reedd053ce92016-03-22 10:17:23 -0700321static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700322 SkImageFilter* imgf = paint.getImageFilter();
323 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700324 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700325 }
326
reedd053ce92016-03-22 10:17:23 -0700327 SkColorFilter* imgCFPtr;
328 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700329 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700330 }
reedd053ce92016-03-22 10:17:23 -0700331 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700332
333 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700334 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700335 // there is no existing paint colorfilter, so we can just return the imagefilter's
336 return imgCF;
337 }
338
339 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
340 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500341 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700342}
343
senorblanco87e066e2015-10-28 11:23:36 -0700344/**
345 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
346 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
347 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
348 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
349 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
350 * conservative "effective" bounds based on the settings in the paint... with one exception. This
351 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
352 * deliberately ignored.
353 */
354static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
355 const SkRect& rawBounds,
356 SkRect* storage) {
357 SkPaint tmpUnfiltered(paint);
358 tmpUnfiltered.setImageFilter(nullptr);
359 if (tmpUnfiltered.canComputeFastBounds()) {
360 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
361 } else {
362 return rawBounds;
363 }
364}
365
Mike Reed38992392019-07-30 10:48:15 -0400366class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367public:
senorblanco87e066e2015-10-28 11:23:36 -0700368 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
369 // paint. It's used to determine the size of the offscreen layer for filters.
370 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400371 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
372 bool skipLayerForImageFilter = false,
373 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400375 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700377 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378
Mike Reed38992392019-07-30 10:48:15 -0400379 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
380 SkASSERT(!fLazyPaint.isValid());
381 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700382 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700383 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700384 fPaint = paint;
385 }
386
387 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700388 /**
389 * We implement ImageFilters for a given draw by creating a layer, then applying the
390 * imagefilter to the pixels of that layer (its backing surface/image), and then
391 * we call restore() to xfer that layer to the main canvas.
392 *
393 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
394 * 2. Generate the src pixels:
395 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
396 * return (fPaint). We then draw the primitive (using srcover) into a cleared
397 * buffer/surface.
398 * 3. Restore the layer created in #1
399 * The imagefilter is passed the buffer/surface from the layer (now filled with the
400 * src pixels of the primitive). It returns a new "filtered" buffer, which we
401 * draw onto the previous layer using the xfermode from the original paint.
402 */
Mike Reed38992392019-07-30 10:48:15 -0400403
404 SkPaint restorePaint;
405 restorePaint.setImageFilter(fPaint->refImageFilter());
406 restorePaint.setBlendMode(fPaint->getBlendMode());
407
senorblanco87e066e2015-10-28 11:23:36 -0700408 SkRect storage;
409 if (rawBounds) {
410 // Make rawBounds include all paint outsets except for those due to image filters.
411 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
412 }
Mike Reed38992392019-07-30 10:48:15 -0400413 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700414 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700415 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000416
Mike Reed38992392019-07-30 10:48:15 -0400417 // Remove the restorePaint fields from our "working" paint
418 SkASSERT(!fLazyPaint.isValid());
419 SkPaint* paint = fLazyPaint.set(origPaint);
420 paint->setImageFilter(nullptr);
421 paint->setBlendMode(SkBlendMode::kSrcOver);
422 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423 }
424 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000425
Mike Reed38992392019-07-30 10:48:15 -0400426 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700427 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000428 fCanvas->internalRestore();
429 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000430 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000432
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433 const SkPaint& paint() const {
434 SkASSERT(fPaint);
435 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438private:
Mike Reed38992392019-07-30 10:48:15 -0400439 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000440 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400441 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000442 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700443 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444};
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446////////// macros to place around the internal draw calls //////////////////
447
Mike Reed38992392019-07-30 10:48:15 -0400448#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700449 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400450 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
451 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800452
453
Mike Reed38992392019-07-30 10:48:15 -0400454#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000455 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400456 AutoLayerForImageFilter draw(this, paint, true); \
457 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000458
Mike Reed38992392019-07-30 10:48:15 -0400459#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000460 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400461 AutoLayerForImageFilter draw(this, paint, false, bounds); \
462 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000463
Mike Reed38992392019-07-30 10:48:15 -0400464#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700465 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400466 AutoLayerForImageFilter draw(this, paint, false, bounds); \
467 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700468
Mike Reed38992392019-07-30 10:48:15 -0400469#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470
471////////////////////////////////////////////////////////////////////////////
472
msarettfbfa2582016-08-12 08:29:08 -0700473static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
474 if (bounds.isEmpty()) {
475 return SkRect::MakeEmpty();
476 }
477
478 // Expand bounds out by 1 in case we are anti-aliasing. We store the
479 // bounds as floats to enable a faster quick reject implementation.
480 SkRect dst;
481 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
482 return dst;
483}
484
mtkleinfeaadee2015-04-08 11:25:48 -0700485void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
486 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700487 fMCRec->reset(bounds);
488
489 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500490 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400491 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700492 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700493 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700494}
495
Hal Canary363a3f82018-10-04 11:04:48 -0400496void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800497 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500500 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500501 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700502 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503
reeda499f902015-05-01 09:34:31 -0700504 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
505 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400506 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509
halcanary96fcdcc2015-08-27 07:41:13 -0700510 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000511
reedf92c8662014-08-18 08:02:43 -0700512 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700513 // The root device and the canvas should always have the same pixel geometry
514 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800515 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700516 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700519 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400520
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500521 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
reed@google.comcde92112011-07-06 20:00:52 +0000524SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700526 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000527{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000528 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000529
Hal Canary363a3f82018-10-04 11:04:48 -0400530 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000531}
532
reed96a857e2015-01-25 10:33:58 -0800533SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800535 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000536{
537 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400538 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500539 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700540}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000541
Hal Canary363a3f82018-10-04 11:04:48 -0400542SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700543 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700544 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700545{
546 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700547
Mike Reed566e53c2017-03-10 10:49:45 -0500548 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400549 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700550}
551
Herb Derbyefe39bc2018-05-01 17:06:20 -0400552SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000553 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700554 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000555{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700557
Hal Canary363a3f82018-10-04 11:04:48 -0400558 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700559}
560
reed4a8126e2014-09-22 07:29:03 -0700561SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700562 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700563 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700564{
565 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700566
Mike Reed910ca0f2018-04-25 13:04:05 -0400567 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400568 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700569}
reed29c857d2014-09-21 10:25:07 -0700570
Mike Reed356f7c22017-01-10 11:58:39 -0500571SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
572 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700573 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
574 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500575 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700576{
577 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700578
Mike Reed910ca0f2018-04-25 13:04:05 -0400579 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400580 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581}
582
Mike Reed356f7c22017-01-10 11:58:39 -0500583SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
584
Matt Sarett31f99ce2017-04-11 08:46:01 -0400585#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
586SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
587 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
588 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
589 , fAllocator(nullptr)
590{
591 inc_canvas();
592
593 SkBitmap tmp(bitmap);
594 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400595 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400596 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400597}
598#endif
599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600SkCanvas::~SkCanvas() {
601 // free up the contents of our deque
602 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 this->internalRestore(); // restore the last, since we're going away
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 dec_canvas();
607}
608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609///////////////////////////////////////////////////////////////////////////////
610
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000611void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700612 this->onFlush();
613}
614
615void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000616 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000617 if (device) {
618 device->flush();
619 }
620}
621
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500622SkSurface* SkCanvas::getSurface() const {
623 return fSurfaceBase;
624}
625
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000626SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000627 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000628 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
629}
630
senorblancoafc7cce2016-02-02 18:44:15 -0800631SkIRect SkCanvas::getTopLayerBounds() const {
632 SkBaseDevice* d = this->getTopDevice();
633 if (!d) {
634 return SkIRect::MakeEmpty();
635 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500636 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800637}
638
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000639SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000641 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400643 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644}
645
Florin Malita0ed3b642017-01-13 16:56:38 +0000646SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400647 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000648}
649
Mike Reed353196f2017-07-21 11:01:18 -0400650bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000651 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400652 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000653}
654
Mike Reed353196f2017-07-21 11:01:18 -0400655bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
656 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400657}
658
659bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
660 SkPixmap pm;
661 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
662}
663
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000664bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400665 SkPixmap pm;
666 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700667 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000668 }
669 return false;
670}
671
Matt Sarett03dd6d52017-01-23 12:15:09 -0500672bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 SkBaseDevice* device = this->getDevice();
675 if (!device) {
676 return false;
677 }
678
Matt Sarett03dd6d52017-01-23 12:15:09 -0500679 // This check gives us an early out and prevents generation ID churn on the surface.
680 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
681 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400682 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500683 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685
Matt Sarett03dd6d52017-01-23 12:15:09 -0500686 // Tell our owning surface to bump its generation ID.
687 const bool completeOverwrite =
688 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700689 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700690
Matt Sarett03dd6d52017-01-23 12:15:09 -0500691 // This can still fail, most notably in the case of a invalid color type or alpha type
692 // conversion. We could pull those checks into this function and avoid the unnecessary
693 // generation ID bump. But then we would be performing those checks twice, since they
694 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400695 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000696}
reed@google.com51df9e32010-12-23 19:29:18 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698//////////////////////////////////////////////////////////////////////////////
699
reed2ff1fce2014-12-11 07:07:37 -0800700void SkCanvas::checkForDeferredSave() {
701 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800702 this->doSave();
703 }
704}
705
reedf0090cb2014-11-26 08:55:51 -0800706int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800707#ifdef SK_DEBUG
708 int count = 0;
709 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
710 for (;;) {
711 const MCRec* rec = (const MCRec*)iter.next();
712 if (!rec) {
713 break;
714 }
715 count += 1 + rec->fDeferredSaveCount;
716 }
717 SkASSERT(count == fSaveCount);
718#endif
719 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800720}
721
722int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800723 fSaveCount += 1;
724 fMCRec->fDeferredSaveCount += 1;
725 return this->getSaveCount() - 1; // return our prev value
726}
727
728void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800729 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700730
731 SkASSERT(fMCRec->fDeferredSaveCount > 0);
732 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800733 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800734}
735
Mike Reed00a97642020-01-25 18:42:23 -0500736int SkCanvas::experimental_saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500737 // TODO: add a virtual for this, and update clients (e.g. chrome)
738 int n = this->save();
Mike Reed46f5c5f2020-02-20 15:42:29 -0500739 this->concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500740 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500741 return n;
742}
743
Mike Reed00a97642020-01-25 18:42:23 -0500744int SkCanvas::experimental_saveCamera(const SkScalar projection[16],
745 const SkScalar camera[16]) {
746 SkM44 proj, cam;
747 proj.setColMajor(projection);
748 cam.setColMajor(camera);
749 return this->experimental_saveCamera(proj, cam);
750}
751
reedf0090cb2014-11-26 08:55:51 -0800752void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800753 if (fMCRec->fDeferredSaveCount > 0) {
754 SkASSERT(fSaveCount > 1);
755 fSaveCount -= 1;
756 fMCRec->fDeferredSaveCount -= 1;
757 } else {
758 // check for underflow
759 if (fMCStack.count() > 1) {
760 this->willRestore();
761 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700762 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800763 this->internalRestore();
764 this->didRestore();
765 }
reedf0090cb2014-11-26 08:55:51 -0800766 }
767}
768
769void SkCanvas::restoreToCount(int count) {
770 // sanity check
771 if (count < 1) {
772 count = 1;
773 }
mtkleinf0f14112014-12-12 08:46:25 -0800774
reedf0090cb2014-11-26 08:55:51 -0800775 int n = this->getSaveCount() - count;
776 for (int i = 0; i < n; ++i) {
777 this->restore();
778 }
779}
780
reed2ff1fce2014-12-11 07:07:37 -0800781void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700783 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000785
Mike Reedc42a1cd2017-02-14 14:25:14 -0500786 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787}
788
reed4960eee2015-12-18 07:09:18 -0800789bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400790 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791}
792
reed4960eee2015-12-18 07:09:18 -0800793bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700794 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400795 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
796 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
797 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
798 // filter does not require a decomposed CTM matrix, the filter space and device space are the
799 // same. When it has been decomposed, we want the original image filter node to process the
800 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
801 // longer the original filter, but has the remainder matrix baked into it, and passing in the
802 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
803 // to the original filter node (barring inflation from consecutive calls to mapRect). While
804 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
805 // passing getDeviceClipBounds() to 'imageFilter' is correct.
806 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
807 // be important to more accurately calculate the clip bounds in the layer space for the original
808 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500809 SkIRect clipBounds = this->getDeviceClipBounds();
810 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000811 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000812 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000813
reed96e657d2015-03-10 17:30:07 -0700814 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
815
Robert Phillips12078432018-05-17 11:17:39 -0400816 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
817 // If the image filter DAG affects transparent black then we will need to render
818 // out to the clip bounds
819 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000820 }
Robert Phillips12078432018-05-17 11:17:39 -0400821
822 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700823 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700825 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400826 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400828 inputSaveLayerBounds = clipBounds;
829 }
830
831 if (imageFilter) {
832 // expand the clip bounds by the image filter DAG to include extra content that might
833 // be required by the image filters.
834 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
835 SkImageFilter::kReverse_MapDirection,
836 &inputSaveLayerBounds);
837 }
838
839 SkIRect clippedSaveLayerBounds;
840 if (bounds) {
841 // For better or for worse, user bounds currently act as a hard clip on the layer's
842 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
843 clippedSaveLayerBounds = inputSaveLayerBounds;
844 } else {
845 // If there are no user bounds, we don't want to artificially restrict the resulting
846 // layer bounds, so allow the expanded clip bounds free reign.
847 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800849
850 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400851 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800852 if (BoundsAffectsClip(saveLayerFlags)) {
853 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
854 fMCRec->fRasterClip.setEmpty();
855 fDeviceClipBounds.setEmpty();
856 }
857 return false;
858 }
Robert Phillips12078432018-05-17 11:17:39 -0400859 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860
reed4960eee2015-12-18 07:09:18 -0800861 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700862 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400863 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
864 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000865 }
866
867 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400868 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000869 }
Robert Phillips12078432018-05-17 11:17:39 -0400870
junov@chromium.orga907ac32012-02-24 21:54:07 +0000871 return true;
872}
873
reed4960eee2015-12-18 07:09:18 -0800874int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
875 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000876}
877
Cary Clarke041e312018-03-06 13:00:52 -0500878int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700879 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400880 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
881 // no need for the layer (or any of the draws until the matching restore()
882 this->save();
883 this->clipRect({0,0,0,0});
884 } else {
885 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
886 fSaveCount += 1;
887 this->internalSaveLayer(rec, strategy);
888 }
reed4960eee2015-12-18 07:09:18 -0800889 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800890}
891
Mike Reed148b7fd2018-12-18 17:38:18 -0500892int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
893 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
894 // Assuming clips never expand, if the request bounds is outside of the current clip
895 // there is no need to copy/restore the area, so just devolve back to a regular save.
896 this->save();
897 } else {
898 bool doTheWork = this->onDoSaveBehind(bounds);
899 fSaveCount += 1;
900 this->internalSave();
901 if (doTheWork) {
902 this->internalSaveBehind(bounds);
903 }
904 }
905 return this->getSaveCount() - 1;
906}
907
reeda2217ef2016-07-20 06:04:34 -0700908void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500909 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500910 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400911 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
912 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400913 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500914 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
915 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400916 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400917 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
918 // This means that we only have to copy a dst-sized block of pixels out of src and translate
919 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400920 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
921 dstOrigin.y() - src->getOrigin().y(),
922 dst->width(), dst->height());
923 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400924 return;
925 }
926
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400927 auto special = src->snapSpecial(snapBounds);
928 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400929 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
930 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400931 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
932 }
933 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400934 }
935
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400936 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
937 // by the backdrop filter.
938 SkMatrix toRoot, layerMatrix;
939 SkSize scale;
940 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
941 toRoot = SkMatrix::I();
942 layerMatrix = ctm;
943 } else if (ctm.decomposeScale(&scale, &toRoot)) {
944 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
945 } else {
946 // Perspective, for now, do no scaling of the layer itself.
947 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
948 // the matrix, e.g. based on the midpoint of the near/far planes?
949 toRoot = ctm;
950 layerMatrix = SkMatrix::I();
951 }
952
953 // We have to map the dst bounds from the root space into the layer space where filtering will
954 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
955 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
956 // is a safe, conservative estimate.
957 SkMatrix fromRoot;
958 if (!toRoot.invert(&fromRoot)) {
959 return;
960 }
961
962 // This represents what the backdrop filter needs to produce in the layer space, and is sized
963 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
964 SkIRect layerTargetBounds = fromRoot.mapRect(
965 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
966 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
967 // require some extra input pixels.
968 SkIRect layerInputBounds = filter->filterBounds(
969 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
970 &layerTargetBounds);
971
972 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400973 // be the conservative contents required to fill a layerInputBounds-sized surface with the
974 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400975 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
976 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
977 if (!backdropBounds.intersect(srcDevRect)) {
978 return;
979 }
980
981 auto special = src->snapSpecial(backdropBounds);
982 if (!special) {
983 return;
984 }
985
986 SkColorType colorType = src->imageInfo().colorType();
987 if (colorType == kUnknown_SkColorType) {
988 colorType = kRGBA_8888_SkColorType;
989 }
990 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400991
992 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400993 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400994 // Drawing the temporary and final filtered image requires a higher filter quality if the
995 // 'toRoot' transformation is not identity, in order to minimize the impact on already
996 // rendered edges/content.
997 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
998 p.setFilterQuality(kHigh_SkFilterQuality);
999
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001000 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1001 // and stored in a temporary surface, which is then used as the input to the actual filter.
1002 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1003 if (!tmpSurface) {
1004 return;
1005 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001006
1007 auto tmpCanvas = tmpSurface->getCanvas();
1008 tmpCanvas->clear(SK_ColorTRANSPARENT);
1009 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1010 // space, then maps from root space into the layer space, then maps it so the input layer's
1011 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1012 // performed on backdropBounds.
1013 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1014 tmpCanvas->concat(fromRoot);
1015 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001016
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001017 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1018 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1019 special = tmpSurface->makeImageSnapshot();
1020 } else {
1021 // Since there is no extra transform that was done, update the input bounds to reflect
1022 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1023 // was equal to backdropBounds before it was made relative to the src device and cropped.
1024 // When we use the original snapped image directly, just map the update backdrop bounds
1025 // back into the shared layer space
1026 layerInputBounds = backdropBounds;
1027 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001028
1029 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1030 // draw will be 1-1 so there is no need to increase filter quality.
1031 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001032 }
1033
1034 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1035 // layer space. This has to further offset everything so that filter evaluation thinks the
1036 // source image's top left corner is (0, 0).
1037 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1038 // this can be simplified.
1039 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1040 SkMatrix filterCTM = layerMatrix;
1041 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1042 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1043
1044 SkIPoint offset;
1045 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001046 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001047 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1048 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1049 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1050 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001051 offset += layerInputBounds.topLeft();
1052
1053 // Manually setting the device's CTM requires accounting for the device's origin.
1054 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001055 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001056 // a global CTM instead of a device CTM.
1057 SkMatrix dstCTM = toRoot;
1058 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001059 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001060
1061 // And because devices don't have a special-image draw function that supports arbitrary
1062 // matrices, we are abusing the asImage() functionality here...
1063 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001064 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001065 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001066 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001067 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1068 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001069 }
robertphillips7354a4b2015-12-16 05:08:27 -08001070}
reed70ee31b2015-12-10 13:44:45 -08001071
Mike Kleine083f7c2018-02-07 12:54:27 -05001072static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001073 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001074 if (prev.bytesPerPixel() <= 4 &&
1075 prev.colorType() != kRGBA_8888_SkColorType &&
1076 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001077 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1078 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1079 ct = kN32_SkColorType;
1080 }
1081 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001082}
1083
reed4960eee2015-12-18 07:09:18 -08001084void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001085 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001086 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001087 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1088
Michael Ludwigeced98b2020-03-03 10:39:41 -05001089 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1090 // saveLayer ignores mask filters, so force it to null
1091 if (paint.get() && paint->getMaskFilter()) {
1092 paint.writable()->setMaskFilter(nullptr);
1093 }
1094
Mike Reed5532c2a2019-02-23 12:00:32 -05001095 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1096 // regardless of any hint-rect from the caller. skbug.com/8783
1097 if (rec.fBackdrop) {
1098 bounds = nullptr;
1099 }
1100
Michael Ludwigeced98b2020-03-03 10:39:41 -05001101 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001102 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001103 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001104
reed8c30a812016-04-20 16:36:51 -07001105 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001106 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1107 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1108 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001109 *
1110 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001111 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1112 * if necessary.
1113 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1114 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001115 * 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 -04001116 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001117 * of the original imagefilter, and draw that (via drawSprite)
1118 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1119 *
1120 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1121 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1122 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001123 if (imageFilter) {
1124 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001125 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1126 &modifiedCTM);
1127 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001128 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001129 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001130 modifiedRec = fMCRec;
1131 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001132 imageFilter = modifiedFilter.get();
1133 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001134 }
1135 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1136 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001137 }
reed8c30a812016-04-20 16:36:51 -07001138
junov@chromium.orga907ac32012-02-24 21:54:07 +00001139 // do this before we create the layer. We don't call the public save() since
1140 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001141 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001142
junov@chromium.orga907ac32012-02-24 21:54:07 +00001143 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001144 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001145 if (modifiedRec) {
1146 // In this case there will be no layer in which to stash the matrix so we need to
1147 // revert the prior MCRec to its earlier state.
1148 modifiedRec->fMatrix = stashedMatrix;
1149 }
reed2ff1fce2014-12-11 07:07:37 -08001150 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 }
1152
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001153 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1154 // the clipRectBounds() call above?
1155 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001156 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001157 }
1158
reed8dc0ccb2015-03-20 06:32:52 -07001159 SkPixelGeometry geo = fProps.pixelGeometry();
1160 if (paint) {
reed76033be2015-03-14 10:54:31 -07001161 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001162 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001163 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001164 }
1165 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166
robertphillips5139e502016-07-19 05:10:40 -07001167 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001168 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001169 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001170 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001171 }
reedb2db8982014-11-13 12:41:02 -08001172
Mike Kleine083f7c2018-02-07 12:54:27 -05001173 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001174 if (rec.fSaveLayerFlags & kF16ColorType) {
1175 info = info.makeColorType(kRGBA_F16_SkColorType);
1176 }
reed129ed1c2016-02-22 06:42:31 -08001177
Hal Canary704cd322016-11-07 14:13:52 -05001178 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001179 {
Florin Malita4571e492019-07-16 10:25:58 -04001180 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001181 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001182 const bool trackCoverage =
1183 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001184 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001185 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001186 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001187 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1188 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001189 return;
reed61f501f2015-04-29 08:34:00 -07001190 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001191 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001192 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193
Mike Reedb43a3e02017-02-11 10:18:58 -05001194 // only have a "next" if this new layer doesn't affect the clip (rare)
1195 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 fMCRec->fLayer = layer;
1197 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001198
Mike Reedc61abee2017-02-28 17:45:27 -05001199 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001200 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001201 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001202 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001203
Mike Reedc42a1cd2017-02-14 14:25:14 -05001204 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1205
1206 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1207 if (layer->fNext) {
1208 // need to punch a hole in the previous device, so we don't draw there, given that
1209 // the new top-layer will allow drawing to happen "below" it.
1210 SkRegion hole(ir);
1211 do {
1212 layer = layer->fNext;
1213 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1214 } while (layer->fNext);
1215 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216}
1217
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001218int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001219 if (0xFF == alpha) {
1220 return this->saveLayer(bounds, nullptr);
1221 } else {
1222 SkPaint tmpPaint;
1223 tmpPaint.setAlpha(alpha);
1224 return this->saveLayer(bounds, &tmpPaint);
1225 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001226}
1227
Mike Reed148b7fd2018-12-18 17:38:18 -05001228void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001229 SkBaseDevice* device = this->getTopDevice();
1230 if (nullptr == device) { // Do we still need this check???
1231 return;
1232 }
1233
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001234 // Map the local bounds into the top device's coordinate space (this is not
1235 // necessarily the full global CTM transform).
1236 SkIRect devBounds;
1237 if (localBounds) {
1238 SkRect tmp;
1239 device->localToDevice().mapRect(&tmp, *localBounds);
1240 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1241 devBounds.setEmpty();
1242 }
1243 } else {
1244 devBounds = device->devClipBounds();
1245 }
1246 if (devBounds.isEmpty()) {
1247 return;
1248 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001249
Michael Ludwigac352122019-08-28 21:03:05 +00001250 // This is getting the special image from the current device, which is then drawn into (both by
1251 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1252 // own device, we need to explicitly copy the back image contents so that its original content
1253 // is available when we splat it back later during restore.
1254 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001255 if (!backImage) {
1256 return;
1257 }
1258
1259 // we really need the save, so we can wack the fMCRec
1260 this->checkForDeferredSave();
1261
1262 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1263
1264 SkPaint paint;
1265 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001266 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001267}
1268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269void SkCanvas::internalRestore() {
1270 SkASSERT(fMCStack.count() != 0);
1271
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001272 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 DeviceCM* layer = fMCRec->fLayer; // may be null
1274 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001275 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276
Mike Reed148b7fd2018-12-18 17:38:18 -05001277 // move this out before we do the actual restore
1278 auto backImage = std::move(fMCRec->fBackImage);
1279
Mike Reedb18e74d2020-01-16 13:58:22 -05001280 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1281 fCameraStack.pop_back();
1282 }
1283
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 // now do the normal restore()
1285 fMCRec->~MCRec(); // balanced in save()
1286 fMCStack.pop_back();
1287 fMCRec = (MCRec*)fMCStack.back();
1288
Mike Reedc42a1cd2017-02-14 14:25:14 -05001289 if (fMCRec) {
1290 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1291 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001292
Mike Reed148b7fd2018-12-18 17:38:18 -05001293 if (backImage) {
1294 SkPaint paint;
1295 paint.setBlendMode(SkBlendMode::kDstOver);
1296 const int x = backImage->fLoc.x();
1297 const int y = backImage->fLoc.y();
1298 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1299 nullptr, SkMatrix::I());
1300 }
1301
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1303 since if we're being recorded, we don't want to record this (the
1304 recorder will have already recorded the restore).
1305 */
bsalomon49f085d2014-09-05 13:34:00 -07001306 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001307 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001308 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001309 // At this point, 'layer' has been removed from the device stack, so the devices that
1310 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1311 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001312 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001313 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001314 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001315 delete layer;
reedb679ca82015-04-07 04:40:48 -07001316 } else {
1317 // we're at the root
reeda499f902015-05-01 09:34:31 -07001318 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001319 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001320 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001322 }
msarettfbfa2582016-08-12 08:29:08 -07001323
1324 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001325 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001326 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1327 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328}
1329
reede8f30622016-03-23 18:59:25 -07001330sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001331 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001332 props = &fProps;
1333 }
1334 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001335}
1336
reede8f30622016-03-23 18:59:25 -07001337sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001338 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001339 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001340}
1341
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001342SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001343 return this->onImageInfo();
1344}
1345
1346SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001347 SkBaseDevice* dev = this->getDevice();
1348 if (dev) {
1349 return dev->imageInfo();
1350 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001351 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001352 }
1353}
1354
brianosman898235c2016-04-06 07:38:23 -07001355bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001356 return this->onGetProps(props);
1357}
1358
1359bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001360 SkBaseDevice* dev = this->getDevice();
1361 if (dev) {
1362 if (props) {
1363 *props = fProps;
1364 }
1365 return true;
1366 } else {
1367 return false;
1368 }
1369}
1370
reed6ceeebd2016-03-09 14:26:26 -08001371bool SkCanvas::peekPixels(SkPixmap* pmap) {
1372 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373}
1374
reed884e97c2015-05-26 11:31:54 -07001375bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001376 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001377 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001378}
1379
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001380void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001381 SkPixmap pmap;
1382 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001383 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001384 }
1385 if (info) {
1386 *info = pmap.info();
1387 }
1388 if (rowBytes) {
1389 *rowBytes = pmap.rowBytes();
1390 }
1391 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001392 // If the caller requested the origin, they presumably are expecting the returned pixels to
1393 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1394 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1395 // instead of an origin, just don't expose the pixels in that case. Note that this means
1396 // that layers with complex coordinate spaces can still report their pixels if the caller
1397 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1398 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1399 *origin = this->getTopDevice()->getOrigin();
1400 } else {
1401 return nullptr;
1402 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001403 }
reed884e97c2015-05-26 11:31:54 -07001404 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001405}
1406
reed884e97c2015-05-26 11:31:54 -07001407bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001408 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001409 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001410}
1411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413
Mike Reed8bcd1282019-03-13 16:51:54 -04001414// In our current design/features, we should never have a layer (src) in a different colorspace
1415// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1416// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1417// colorspace.
1418static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1419 SkASSERT(src == dst);
1420}
1421
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001422void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001423 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001425 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 paint = &tmp;
1427 }
reed@google.com4b226022011-01-11 18:32:13 +00001428
Mike Reed38992392019-07-30 10:48:15 -04001429 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001430
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001432 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001433 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1434 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001435 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001436 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001437 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1438 // so it should always be possible to use the relative origin. Once drawDevice() and
1439 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1440 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001441 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001442 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1443 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001444 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1445 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001446 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1447 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001448 }
reed@google.com76dd2772012-01-05 21:15:07 +00001449 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001450 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001451 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 }
reeda2217ef2016-07-20 06:04:34 -07001453
Mike Reed38992392019-07-30 10:48:15 -04001454 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455}
1456
reed32704672015-12-16 08:27:10 -08001457/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001458
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001459void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001460 if (dx || dy) {
1461 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001462 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001463
reedfe69b502016-09-12 06:31:48 -07001464 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001465 // However, if either is non-finite, we might still complicate the matrix type,
1466 // so we still have to compute this.
1467 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001468
Mike Reedc42a1cd2017-02-14 14:25:14 -05001469 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001470
reedfe69b502016-09-12 06:31:48 -07001471 this->didTranslate(dx,dy);
1472 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473}
1474
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001475void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001476 if (sx != 1 || sy != 1) {
1477 this->checkForDeferredSave();
1478 fMCRec->fMatrix.preScale(sx, sy);
1479
1480 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1481 // but pre-scaling by a non-finite does change it, so we have to recompute.
1482 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1483
1484 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1485
1486 this->didScale(sx, sy);
1487 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488}
1489
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001490void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001491 SkMatrix m;
1492 m.setRotate(degrees);
1493 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494}
1495
bungeman7438bfc2016-07-12 15:01:19 -07001496void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1497 SkMatrix m;
1498 m.setRotate(degrees, px, py);
1499 this->concat(m);
1500}
1501
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001502void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001503 SkMatrix m;
1504 m.setSkew(sx, sy);
1505 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001506}
1507
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001508void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001509 if (matrix.isIdentity()) {
1510 return;
1511 }
1512
reed2ff1fce2014-12-11 07:07:37 -08001513 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001514 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001515
msarett9637ea92016-08-18 14:03:30 -07001516 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001517
Mike Reed7627fa52017-02-08 10:07:53 -05001518 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001519
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001520 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001521}
1522
Mike Reed46f5c5f2020-02-20 15:42:29 -05001523void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001524 this->checkForDeferredSave();
1525
Mike Reed46f5c5f2020-02-20 15:42:29 -05001526 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001527
1528 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1529
1530 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1531
Mike Reed46f5c5f2020-02-20 15:42:29 -05001532 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001533}
Mike Reed064c7f92020-01-08 17:33:04 -05001534
Mike Reed46f5c5f2020-02-20 15:42:29 -05001535void SkCanvas::concat44(const SkM44& m) {
1536 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001537}
1538
reed8c30a812016-04-20 16:36:51 -07001539void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001540 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001541 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001542
Mike Reedc42a1cd2017-02-14 14:25:14 -05001543 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001544}
1545
1546void SkCanvas::setMatrix(const SkMatrix& matrix) {
1547 this->checkForDeferredSave();
1548 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001549 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550}
1551
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001553 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554}
1555
1556//////////////////////////////////////////////////////////////////////////////
1557
Mike Reedc1f77742016-12-09 09:00:50 -05001558void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001559 if (!rect.isFinite()) {
1560 return;
1561 }
reed2ff1fce2014-12-11 07:07:37 -08001562 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1564 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565}
1566
Mike Reedc1f77742016-12-09 09:00:50 -05001567void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001568 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001569
Mike Reed7627fa52017-02-08 10:07:53 -05001570 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001571
reedc64eff52015-11-21 12:39:45 -08001572 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001573 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1574 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001575 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576}
1577
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001578void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1579 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001580 if (fClipRestrictionRect.isEmpty()) {
1581 // we notify the device, but we *dont* resolve deferred saves (since we're just
1582 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001583 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001584 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001585 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001586 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001587 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001588 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001589 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1590 }
1591}
1592
Mike Reedc1f77742016-12-09 09:00:50 -05001593void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001594 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001596 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001597 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1598 } else {
1599 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001600 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001601}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001602
Mike Reedc1f77742016-12-09 09:00:50 -05001603void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001604 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001605
Brian Salomona3b45d42016-10-03 11:36:16 -04001606 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001607
Mike Reed7627fa52017-02-08 10:07:53 -05001608 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001609
Mike Reed20800c82017-11-15 16:09:04 -05001610 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1611 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001612 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001613}
1614
Mike Reedc1f77742016-12-09 09:00:50 -05001615void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001616 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001617 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001618
1619 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1620 SkRect r;
1621 if (path.isRect(&r)) {
1622 this->onClipRect(r, op, edgeStyle);
1623 return;
1624 }
1625 SkRRect rrect;
1626 if (path.isOval(&r)) {
1627 rrect.setOval(r);
1628 this->onClipRRect(rrect, op, edgeStyle);
1629 return;
1630 }
1631 if (path.isRRect(&rrect)) {
1632 this->onClipRRect(rrect, op, edgeStyle);
1633 return;
1634 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635 }
robertphillips39f05382015-11-24 09:30:12 -08001636
1637 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001638}
1639
Mike Reedc1f77742016-12-09 09:00:50 -05001640void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001641 AutoValidateClip avc(this);
1642
Brian Salomona3b45d42016-10-03 11:36:16 -04001643 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001644
Mike Reed7627fa52017-02-08 10:07:53 -05001645 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646
Brian Salomona3b45d42016-10-03 11:36:16 -04001647 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001648 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001649 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001650 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651}
1652
Mike Reed121c2af2020-03-10 14:02:56 -04001653void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1654 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001655 if (sh->isOpaque()) {
1656 if (op == SkClipOp::kIntersect) {
1657 // we don't occlude anything, so skip this call
1658 } else {
1659 SkASSERT(op == SkClipOp::kDifference);
1660 // we occlude everything, so set the clip to empty
1661 this->clipRect({0,0,0,0});
1662 }
1663 } else {
1664 this->onClipShader(std::move(sh), op);
1665 }
Mike Reed121c2af2020-03-10 14:02:56 -04001666 }
1667}
1668
1669void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1670 AutoValidateClip avc(this);
1671
1672 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1673
1674 // we don't know how to mutate our conservative bounds, so we don't
1675}
1676
Mike Reedc1f77742016-12-09 09:00:50 -05001677void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001678 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001680}
1681
Mike Reedc1f77742016-12-09 09:00:50 -05001682void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001683 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001684
reed@google.com5c3d1472011-02-22 19:12:23 +00001685 AutoValidateClip avc(this);
1686
Mike Reed20800c82017-11-15 16:09:04 -05001687 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001688 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689}
1690
reed@google.com819c9212011-02-23 18:56:55 +00001691#ifdef SK_DEBUG
1692void SkCanvas::validateClip() const {
1693 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001694 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001695 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001696 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001697 return;
1698 }
reed@google.com819c9212011-02-23 18:56:55 +00001699}
1700#endif
1701
Mike Reeda1361362017-03-07 09:37:29 -05001702bool SkCanvas::androidFramework_isClipAA() const {
1703 bool containsAA = false;
1704
1705 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1706
1707 return containsAA;
1708}
1709
1710class RgnAccumulator {
1711 SkRegion* fRgn;
1712public:
1713 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1714 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1715 SkIPoint origin = device->getOrigin();
1716 if (origin.x() | origin.y()) {
1717 rgn->translate(origin.x(), origin.y());
1718 }
1719 fRgn->op(*rgn, SkRegion::kUnion_Op);
1720 }
1721};
1722
1723void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1724 RgnAccumulator accum(rgn);
1725 SkRegion tmp;
1726
1727 rgn->setEmpty();
1728 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001729}
1730
reed@google.com5c3d1472011-02-22 19:12:23 +00001731///////////////////////////////////////////////////////////////////////////////
1732
reed@google.com754de5f2014-02-24 19:38:20 +00001733bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001734 return fMCRec->fRasterClip.isEmpty();
1735
1736 // TODO: should we only use the conservative answer in a recording canvas?
1737#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001738 SkBaseDevice* dev = this->getTopDevice();
1739 // if no device we return true
1740 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001741#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001742}
1743
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001744bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001745 SkBaseDevice* dev = this->getTopDevice();
1746 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001747 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001748}
1749
msarettfbfa2582016-08-12 08:29:08 -07001750static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1751#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1752 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1753 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1754 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1755 return 0xF != _mm_movemask_ps(mask);
1756#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1757 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1758 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1759 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1760 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1761#else
1762 SkRect devRectAsRect;
1763 SkRect devClipAsRect;
1764 devRect.store(&devRectAsRect.fLeft);
1765 devClip.store(&devClipAsRect.fLeft);
1766 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1767#endif
1768}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001769
msarettfbfa2582016-08-12 08:29:08 -07001770// It's important for this function to not be inlined. Otherwise the compiler will share code
1771// between the fast path and the slow path, resulting in two slow paths.
1772static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1773 const SkMatrix& matrix) {
1774 SkRect deviceRect;
1775 matrix.mapRect(&deviceRect, src);
1776 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1777}
1778
1779bool SkCanvas::quickReject(const SkRect& src) const {
1780#ifdef SK_DEBUG
1781 // Verify that fDeviceClipBounds are set properly.
1782 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001783 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001784 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001785 } else {
msarettfbfa2582016-08-12 08:29:08 -07001786 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 }
msarettfbfa2582016-08-12 08:29:08 -07001788
msarett9637ea92016-08-18 14:03:30 -07001789 // Verify that fIsScaleTranslate is set properly.
1790 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001791#endif
1792
msarett9637ea92016-08-18 14:03:30 -07001793 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001794 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1795 }
1796
1797 // We inline the implementation of mapScaleTranslate() for the fast path.
1798 float sx = fMCRec->fMatrix.getScaleX();
1799 float sy = fMCRec->fMatrix.getScaleY();
1800 float tx = fMCRec->fMatrix.getTranslateX();
1801 float ty = fMCRec->fMatrix.getTranslateY();
1802 Sk4f scale(sx, sy, sx, sy);
1803 Sk4f trans(tx, ty, tx, ty);
1804
1805 // Apply matrix.
1806 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1807
1808 // Make sure left < right, top < bottom.
1809 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1810 Sk4f min = Sk4f::Min(ltrb, rblt);
1811 Sk4f max = Sk4f::Max(ltrb, rblt);
1812 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1813 // ARM this sequence generates the fastest (a single instruction).
1814 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1815
1816 // Check if the device rect is NaN or outside the clip.
1817 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818}
1819
reed@google.com3b3e8952012-08-16 20:53:31 +00001820bool SkCanvas::quickReject(const SkPath& path) const {
1821 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822}
1823
Mike Klein83c8dd92017-11-28 17:08:45 -05001824SkRect SkCanvas::getLocalClipBounds() const {
1825 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001826 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001827 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828 }
1829
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001830 SkMatrix inverse;
1831 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001832 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001833 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001834 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835
Mike Reed42e8c532017-01-23 14:09:13 -05001836 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001837 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001838 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001839
Mike Reedb57b9312018-04-23 12:12:54 -04001840 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001841 inverse.mapRect(&bounds, r);
1842 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843}
1844
Mike Klein83c8dd92017-11-28 17:08:45 -05001845SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001846 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001847}
1848
Mike Reedb18e74d2020-01-16 13:58:22 -05001849///////////////////////////////////////////////////////////////////////
1850
1851SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1852 : fMCRec(owner)
1853 , fCamera(camera)
1854{
1855 // assumes the mcrec has already been concatenated with the camera
1856 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1857 fInvPostCamera.setIdentity();
1858 }
1859}
1860
Mike Reed403c8072020-01-08 10:40:39 -05001861SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001862 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863}
1864
Mike Reed46f5c5f2020-02-20 15:42:29 -05001865SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001866 return fMCRec->fMatrix;
1867}
1868
Mike Reedc43f2a02020-01-16 14:54:34 -05001869SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001870 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001871 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001872 } else {
1873 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001874 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001875 }
1876}
1877
Mike Reedc43f2a02020-01-16 14:54:34 -05001878SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001879 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001880 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001881 } else {
1882 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001883 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001884 }
1885}
1886
Mike Reed46f5c5f2020-02-20 15:42:29 -05001887void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1888 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001889}
1890
1891void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1892 this->experimental_getLocalToWorld().getColMajor(colMajor);
1893}
1894
1895void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1896 this->experimental_getLocalToCamera().getColMajor(colMajor);
1897}
1898
Brian Osman11052242016-10-27 14:47:55 -04001899GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001900 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001901 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001902}
1903
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001904GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001905 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001906 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001907}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001908
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001909void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1910 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001911 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001912 if (outer.isEmpty()) {
1913 return;
1914 }
1915 if (inner.isEmpty()) {
1916 this->drawRRect(outer, paint);
1917 return;
1918 }
1919
1920 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001921 // be able to return ...
1922 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001923 //
1924 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001925 if (!outer.getBounds().contains(inner.getBounds())) {
1926 return;
1927 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001928
1929 this->onDrawDRRect(outer, inner, paint);
1930}
1931
reed41af9662015-01-05 07:49:08 -08001932void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001933 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001934 this->onDrawPaint(paint);
1935}
1936
1937void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001938 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001939 // To avoid redundant logic in our culling code and various backends, we always sort rects
1940 // before passing them along.
1941 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001942}
1943
Mike Reedd5674082019-04-19 15:00:47 -04001944void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1945 TRACE_EVENT0("skia", TRACE_FUNC);
1946 this->onDrawBehind(paint);
1947}
1948
msarettdca352e2016-08-26 06:37:45 -07001949void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001950 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001951 if (region.isEmpty()) {
1952 return;
1953 }
1954
1955 if (region.isRect()) {
1956 return this->drawIRect(region.getBounds(), paint);
1957 }
1958
1959 this->onDrawRegion(region, paint);
1960}
1961
reed41af9662015-01-05 07:49:08 -08001962void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001963 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001964 // To avoid redundant logic in our culling code and various backends, we always sort rects
1965 // before passing them along.
1966 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001967}
1968
1969void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001970 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001971 this->onDrawRRect(rrect, paint);
1972}
1973
1974void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001975 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001976 this->onDrawPoints(mode, count, pts, paint);
1977}
1978
Mike Reede88a1cb2017-03-17 09:50:46 -04001979void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1980 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001981 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001982}
1983
1984void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001985 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001986 RETURN_ON_NULL(vertices);
Mike Reed5caf9352020-03-02 14:57:09 -05001987 // We expect fans to be converted to triangles when building or deserializing SkVertices.
Mike Reedba962562020-03-12 20:33:21 -04001988 SkASSERT(SkVerticesPriv::Mode(vertices) != SkVertices::kTriangleFan_VertexMode);
Mike Reed5caf9352020-03-02 14:57:09 -05001989 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001990}
1991
1992void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001993 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001994 this->onDrawPath(path, paint);
1995}
1996
reeda85d4d02015-05-06 12:56:48 -07001997void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001998 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001999 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07002000 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08002001}
2002
Mike Reedc4e31092018-01-30 11:15:27 -05002003// Returns true if the rect can be "filled" : non-empty and finite
2004static bool fillable(const SkRect& r) {
2005 SkScalar w = r.width();
2006 SkScalar h = r.height();
2007 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
2008}
2009
reede47829b2015-08-06 10:02:53 -07002010void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2011 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002012 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002013 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002014 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002015 return;
2016 }
2017 this->onDrawImageRect(image, &src, dst, paint, constraint);
2018}
reed41af9662015-01-05 07:49:08 -08002019
reed84984ef2015-07-17 07:09:43 -07002020void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2021 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002022 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002023 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002024}
2025
Brian Salomonf08002c2018-10-26 16:15:46 -04002026void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002027 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002028 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002029 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002030}
reede47829b2015-08-06 10:02:53 -07002031
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002032namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002033class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002034public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002035 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2036 if (!origPaint) {
2037 return;
2038 }
2039 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2040 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2041 }
2042 if (origPaint->getMaskFilter()) {
2043 fPaint.writable()->setMaskFilter(nullptr);
2044 }
2045 if (origPaint->isAntiAlias()) {
2046 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002047 }
2048 }
2049
2050 const SkPaint* get() const {
2051 return fPaint;
2052 }
2053
2054private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002055 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002056};
2057} // namespace
2058
reed4c21dc52015-06-25 12:32:03 -07002059void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2060 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002061 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002062 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002063 if (dst.isEmpty()) {
2064 return;
2065 }
msarett552bca92016-08-03 06:53:26 -07002066 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002067 LatticePaint latticePaint(paint);
2068 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002069 } else {
reede47829b2015-08-06 10:02:53 -07002070 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002071 }
reed4c21dc52015-06-25 12:32:03 -07002072}
2073
msarett16882062016-08-16 09:31:08 -07002074void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2075 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002076 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002077 RETURN_ON_NULL(image);
2078 if (dst.isEmpty()) {
2079 return;
2080 }
msarett71df2d72016-09-30 12:41:42 -07002081
2082 SkIRect bounds;
2083 Lattice latticePlusBounds = lattice;
2084 if (!latticePlusBounds.fBounds) {
2085 bounds = SkIRect::MakeWH(image->width(), image->height());
2086 latticePlusBounds.fBounds = &bounds;
2087 }
2088
2089 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002090 LatticePaint latticePaint(paint);
2091 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002092 } else {
2093 this->drawImageRect(image, dst, paint);
2094 }
2095}
2096
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002097static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002098 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002099 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002100 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002101 return SkImage::MakeFromBitmap(bitmap);
2102}
2103
2104void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2105 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002106}
2107
reede47829b2015-08-06 10:02:53 -07002108void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002109 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002110 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002111}
2112
reed84984ef2015-07-17 07:09:43 -07002113void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2114 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002115 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002116}
2117
reede47829b2015-08-06 10:02:53 -07002118void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2119 SrcRectConstraint constraint) {
2120 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2121 constraint);
2122}
reede47829b2015-08-06 10:02:53 -07002123
reed71c3c762015-06-24 10:29:17 -07002124void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002125 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002126 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002127 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002128 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002129 if (count <= 0) {
2130 return;
2131 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002132 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002133 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002134 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002135}
2136
reedf70b5312016-03-04 16:36:20 -08002137void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002138 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002139 if (key) {
2140 this->onDrawAnnotation(rect, key, value);
2141 }
2142}
2143
reede47829b2015-08-06 10:02:53 -07002144void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2145 const SkPaint* paint, SrcRectConstraint constraint) {
2146 if (src) {
2147 this->drawImageRect(image, *src, dst, paint, constraint);
2148 } else {
2149 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2150 dst, paint, constraint);
2151 }
2152}
2153void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2154 const SkPaint* paint, SrcRectConstraint constraint) {
2155 if (src) {
2156 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2157 } else {
2158 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2159 dst, paint, constraint);
2160 }
2161}
2162
Mike Reed4204da22017-05-17 08:53:36 -04002163void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002164 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002165 this->onDrawShadowRec(path, rec);
2166}
2167
2168void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2169 SkPaint paint;
2170 const SkRect& pathBounds = path.getBounds();
2171
Mike Reed38992392019-07-30 10:48:15 -04002172 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002173 while (iter.next()) {
2174 iter.fDevice->drawShadow(path, rec);
2175 }
Mike Reed38992392019-07-30 10:48:15 -04002176 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002177}
2178
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002179void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002180 QuadAAFlags aaFlags, const SkColor4f& color,
2181 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002182 TRACE_EVENT0("skia", TRACE_FUNC);
2183 // Make sure the rect is sorted before passing it along
2184 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2185}
2186
2187void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2188 const SkPoint dstClips[],
2189 const SkMatrix preViewMatrices[],
2190 const SkPaint* paint,
2191 SrcRectConstraint constraint) {
2192 TRACE_EVENT0("skia", TRACE_FUNC);
2193 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2194}
2195
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196//////////////////////////////////////////////////////////////////////////////
2197// These are the virtual drawing methods
2198//////////////////////////////////////////////////////////////////////////////
2199
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002200void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002201 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002202 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2203 }
2204}
2205
reed41af9662015-01-05 07:49:08 -08002206void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002207 this->internalDrawPaint(paint);
2208}
2209
2210void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002211 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002212
2213 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002214 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215 }
2216
Mike Reed38992392019-07-30 10:48:15 -04002217 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218}
2219
reed41af9662015-01-05 07:49:08 -08002220void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2221 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 if ((long)count <= 0) {
2223 return;
2224 }
2225
Mike Reed822128b2017-02-28 16:41:03 -05002226 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002227 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002228 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002229 // special-case 2 points (common for drawing a single line)
2230 if (2 == count) {
2231 r.set(pts[0], pts[1]);
2232 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002233 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002234 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002235 if (!r.isFinite()) {
2236 return;
2237 }
Mike Reed822128b2017-02-28 16:41:03 -05002238 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002239 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2240 return;
2241 }
2242 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002243 }
reed@google.coma584aed2012-05-16 14:06:02 +00002244
halcanary96fcdcc2015-08-27 07:41:13 -07002245 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246
Mike Reed38992392019-07-30 10:48:15 -04002247 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002248
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002250 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251 }
reed@google.com4b226022011-01-11 18:32:13 +00002252
Mike Reed38992392019-07-30 10:48:15 -04002253 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254}
2255
reed4a167172016-08-18 17:15:25 -07002256static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002257 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002258}
2259
reed41af9662015-01-05 07:49:08 -08002260void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002261 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002263 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002264 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002265 return;
2266 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002267 }
reed@google.com4b226022011-01-11 18:32:13 +00002268
reed4a167172016-08-18 17:15:25 -07002269 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002270 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271
reed4a167172016-08-18 17:15:25 -07002272 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002273 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002274 }
2275
Mike Reed38992392019-07-30 10:48:15 -04002276 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002277 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002278 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002279 SkDrawIter iter(this);
2280 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002281 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284}
2285
msarett44df6512016-08-25 13:54:30 -07002286void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002287 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002288 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002289 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002290 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2291 return;
2292 }
msarett44df6512016-08-25 13:54:30 -07002293 }
2294
Mike Reed38992392019-07-30 10:48:15 -04002295 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002296
2297 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002298 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002299 }
2300
Mike Reed38992392019-07-30 10:48:15 -04002301 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002302}
2303
Mike Reedd5674082019-04-19 15:00:47 -04002304void SkCanvas::onDrawBehind(const SkPaint& paint) {
2305 SkIRect bounds;
2306 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2307 for (;;) {
2308 const MCRec* rec = (const MCRec*)iter.prev();
2309 if (!rec) {
2310 return; // no backimages, so nothing to draw
2311 }
2312 if (rec->fBackImage) {
2313 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2314 rec->fBackImage->fImage->width(),
2315 rec->fBackImage->fImage->height());
2316 break;
2317 }
2318 }
2319
Mike Reed38992392019-07-30 10:48:15 -04002320 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002321
2322 while (iter.next()) {
2323 SkBaseDevice* dev = iter.fDevice;
2324
Mike Reedd5674082019-04-19 15:00:47 -04002325 dev->save();
2326 // We use clipRegion because it is already defined to operate in dev-space
2327 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2328 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002329 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002330 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002331 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002332 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002333 }
2334
Mike Reed38992392019-07-30 10:48:15 -04002335 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002336}
2337
reed41af9662015-01-05 07:49:08 -08002338void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002339 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002340 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002341 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002342 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002343 return;
2344 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002345 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002346
Mike Reed38992392019-07-30 10:48:15 -04002347 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002348
2349 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002350 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002351 }
2352
Mike Reed38992392019-07-30 10:48:15 -04002353 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002354}
2355
bsalomonac3aa242016-08-19 11:25:19 -07002356void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2357 SkScalar sweepAngle, bool useCenter,
2358 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002359 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002360 if (paint.canComputeFastBounds()) {
2361 SkRect storage;
2362 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002363 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002364 return;
2365 }
bsalomonac3aa242016-08-19 11:25:19 -07002366 }
2367
Mike Reed38992392019-07-30 10:48:15 -04002368 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002369
2370 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002371 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002372 }
2373
Mike Reed38992392019-07-30 10:48:15 -04002374 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002375}
2376
reed41af9662015-01-05 07:49:08 -08002377void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +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(rrect.getBounds(), &storage))) {
2381 return;
2382 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002383 }
2384
2385 if (rrect.isRect()) {
2386 // call the non-virtual version
2387 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002388 return;
2389 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002390 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002391 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2392 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002393 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002394
Mike Reed38992392019-07-30 10:48:15 -04002395 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002396
2397 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002398 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002399 }
2400
Mike Reed38992392019-07-30 10:48:15 -04002401 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002402}
2403
Mike Reed822128b2017-02-28 16:41:03 -05002404void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002405 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002406 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002407 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2408 return;
2409 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002410 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002411
Mike Reed38992392019-07-30 10:48:15 -04002412 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002413
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002414 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002415 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002416 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002417
Mike Reed38992392019-07-30 10:48:15 -04002418 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002419}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002420
reed41af9662015-01-05 07:49:08 -08002421void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002422 if (!path.isFinite()) {
2423 return;
2424 }
2425
Mike Reed822128b2017-02-28 16:41:03 -05002426 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002427 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002428 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002429 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2430 return;
2431 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002433
Mike Reed822128b2017-02-28 16:41:03 -05002434 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002435 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002436 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002437 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002438 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440
Mike Reed38992392019-07-30 10:48:15 -04002441 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442
2443 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002444 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002445 }
2446
Mike Reed38992392019-07-30 10:48:15 -04002447 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448}
2449
reed262a71b2015-12-05 13:07:27 -08002450bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002451 if (!paint.getImageFilter()) {
2452 return false;
2453 }
2454
2455 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002456 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002457 return false;
2458 }
2459
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002460 // The other paint effects need to be applied before the image filter, but the sprite draw
2461 // applies the filter explicitly first.
2462 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2463 return false;
2464 }
reed262a71b2015-12-05 13:07:27 -08002465 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2466 // Once we can filter and the filter will return a result larger than itself, we should be
2467 // able to remove this constraint.
2468 // skbug.com/4526
2469 //
2470 SkPoint pt;
2471 ctm.mapXY(x, y, &pt);
2472 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2473 return ir.contains(fMCRec->fRasterClip.getBounds());
2474}
2475
Mike Reedf441cfc2018-04-11 14:50:16 -04002476// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2477// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2478// null.
2479static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2480 if (paintParam) {
2481 *real = *paintParam;
2482 real->setStyle(SkPaint::kFill_Style);
2483 real->setPathEffect(nullptr);
2484 paintParam = real;
2485 }
2486 return paintParam;
2487}
2488
reeda85d4d02015-05-06 12:56:48 -07002489void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002490 SkPaint realPaint;
2491 paint = init_image_paint(&realPaint, paint);
2492
reeda85d4d02015-05-06 12:56:48 -07002493 SkRect bounds = SkRect::MakeXYWH(x, y,
2494 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002495 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002496 SkRect tmp = bounds;
2497 if (paint) {
2498 paint->computeFastBounds(tmp, &tmp);
2499 }
2500 if (this->quickReject(tmp)) {
2501 return;
2502 }
reeda85d4d02015-05-06 12:56:48 -07002503 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002504 // At this point we need a real paint object. If the caller passed null, then we should
2505 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2506 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2507 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002508
reeda2217ef2016-07-20 06:04:34 -07002509 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002510 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2511 *paint);
2512 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002513 special = this->getDevice()->makeSpecial(image);
2514 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002515 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002516 }
2517 }
2518
Mike Reed38992392019-07-30 10:48:15 -04002519 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002520
reeda85d4d02015-05-06 12:56:48 -07002521 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002522 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002523 if (special) {
2524 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002525 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002526 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002527 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002528 SkScalarRoundToInt(pt.fY), pnt,
2529 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002530 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002531 iter.fDevice->drawImageRect(
2532 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2533 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002534 }
reeda85d4d02015-05-06 12:56:48 -07002535 }
halcanary9d524f22016-03-29 09:03:52 -07002536
Mike Reed38992392019-07-30 10:48:15 -04002537 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002538}
2539
reed41af9662015-01-05 07:49:08 -08002540void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002541 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002542 SkPaint realPaint;
2543 paint = init_image_paint(&realPaint, paint);
2544
halcanary96fcdcc2015-08-27 07:41:13 -07002545 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002546 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002547 if (paint) {
2548 paint->computeFastBounds(dst, &storage);
2549 }
2550 if (this->quickReject(storage)) {
2551 return;
2552 }
reeda85d4d02015-05-06 12:56:48 -07002553 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002554 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002555
Mike Reed38992392019-07-30 10:48:15 -04002556 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002557
reeda85d4d02015-05-06 12:56:48 -07002558 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002559 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002560 }
halcanary9d524f22016-03-29 09:03:52 -07002561
Mike Reed38992392019-07-30 10:48:15 -04002562 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002563}
2564
reed4c21dc52015-06-25 12:32:03 -07002565void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2566 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002567 SkPaint realPaint;
2568 paint = init_image_paint(&realPaint, paint);
2569
halcanary96fcdcc2015-08-27 07:41:13 -07002570 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002571 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002572 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2573 return;
2574 }
reed@google.com3d608122011-11-21 15:16:16 +00002575 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002576 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002577
Mike Reed38992392019-07-30 10:48:15 -04002578 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002579
reed4c21dc52015-06-25 12:32:03 -07002580 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002581 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002582 }
halcanary9d524f22016-03-29 09:03:52 -07002583
Mike Reed38992392019-07-30 10:48:15 -04002584 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002585}
2586
msarett16882062016-08-16 09:31:08 -07002587void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2588 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002589 SkPaint realPaint;
2590 paint = init_image_paint(&realPaint, paint);
2591
msarett16882062016-08-16 09:31:08 -07002592 if (nullptr == paint || paint->canComputeFastBounds()) {
2593 SkRect storage;
2594 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2595 return;
2596 }
2597 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002598 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002599
Mike Reed38992392019-07-30 10:48:15 -04002600 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002601
2602 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002603 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002604 }
2605
Mike Reed38992392019-07-30 10:48:15 -04002606 DRAW_END
msarett16882062016-08-16 09:31:08 -07002607}
2608
fmalita00d5c2c2014-08-21 08:53:26 -07002609void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2610 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002611 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002612 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002613 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002614 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002615 SkRect tmp;
2616 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2617 return;
2618 }
2619 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002620 }
2621
fmalita024f9962015-03-03 19:08:17 -08002622 // We cannot filter in the looper as we normally do, because the paint is
2623 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002624 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002625
fmalitaaa1b9122014-08-28 14:32:24 -07002626 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002627 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002628 }
2629
Mike Reed38992392019-07-30 10:48:15 -04002630 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002631}
2632
Mike Reed358fcad2018-11-23 15:27:51 -05002633// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002634void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002635 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2636 TRACE_EVENT0("skia", TRACE_FUNC);
2637 if (byteLength) {
2638 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002639 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002640 }
2641}
Mike Reed4f81bb72019-01-23 09:23:00 -05002642
fmalita00d5c2c2014-08-21 08:53:26 -07002643void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2644 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002645 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002646 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002647 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002648 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002649}
reed@google.come0d9ce82014-04-23 04:00:17 +00002650
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002651#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002652void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002653 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002654 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002655
2656 while (iter.next()) {
2657 // In the common case of one iteration we could std::move vertices here.
Mike Reed5caf9352020-03-02 14:57:09 -05002658 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002659 }
2660
Mike Reed38992392019-07-30 10:48:15 -04002661 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002662}
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002663#else
2664void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2665 const SkPaint& paint) {
2666 DRAW_BEGIN(paint, nullptr)
2667
2668 while (iter.next()) {
2669 // In the common case of one iteration we could std::move vertices here.
2670 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2671 }
2672
2673 DRAW_END
2674}
2675#endif
Brian Salomon199fb872017-02-06 09:41:10 -05002676
dandovb3c9d1c2014-08-12 08:34:29 -07002677void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002678 const SkPoint texCoords[4], SkBlendMode bmode,
2679 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002680 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002681 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002682 return;
2683 }
mtklein6cfa73a2014-08-13 13:33:49 -07002684
Mike Reedfaba3712016-11-03 14:45:31 -04002685 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002686}
2687
2688void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002689 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002690 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002691 // Since a patch is always within the convex hull of the control points, we discard it when its
2692 // bounding rectangle is completely outside the current clip.
2693 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002694 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002695 if (this->quickReject(bounds)) {
2696 return;
2697 }
mtklein6cfa73a2014-08-13 13:33:49 -07002698
Mike Reed38992392019-07-30 10:48:15 -04002699 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002700
dandovecfff212014-08-04 10:02:00 -07002701 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002702 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002703 }
mtklein6cfa73a2014-08-13 13:33:49 -07002704
Mike Reed38992392019-07-30 10:48:15 -04002705 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002706}
2707
reeda8db7282015-07-07 10:22:31 -07002708void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002709#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002710 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002711#endif
reede3b38ce2016-01-08 09:18:44 -08002712 RETURN_ON_NULL(dr);
2713 if (x || y) {
2714 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2715 this->onDrawDrawable(dr, &matrix);
2716 } else {
2717 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002718 }
2719}
2720
reeda8db7282015-07-07 10:22:31 -07002721void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002722#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002723 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002724#endif
reede3b38ce2016-01-08 09:18:44 -08002725 RETURN_ON_NULL(dr);
2726 if (matrix && matrix->isIdentity()) {
2727 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002728 }
reede3b38ce2016-01-08 09:18:44 -08002729 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002730}
2731
2732void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002733 // drawable bounds are no longer reliable (e.g. android displaylist)
2734 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002735 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002736}
2737
reed71c3c762015-06-24 10:29:17 -07002738void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002739 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002740 const SkRect* cull, const SkPaint* paint) {
2741 if (cull && this->quickReject(*cull)) {
2742 return;
2743 }
2744
2745 SkPaint pnt;
2746 if (paint) {
2747 pnt = *paint;
2748 }
halcanary9d524f22016-03-29 09:03:52 -07002749
Mike Reed38992392019-07-30 10:48:15 -04002750 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002751 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002752 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002753 }
Mike Reed38992392019-07-30 10:48:15 -04002754 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002755}
2756
reedf70b5312016-03-04 16:36:20 -08002757void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2758 SkASSERT(key);
2759
2760 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002761 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002762 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002763 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002764 }
Mike Reed38992392019-07-30 10:48:15 -04002765 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002766}
2767
Michael Ludwiga595f862019-08-27 15:25:49 -04002768void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2769 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002770 SkASSERT(r.isSorted());
2771
2772 // If this used a paint, it would be a filled color with blend mode, which does not
2773 // need to use an autodraw loop, so use SkDrawIter directly.
2774 if (this->quickReject(r)) {
2775 return;
2776 }
2777
Michael Ludwiga4b44882019-08-28 14:34:58 -04002778 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002779 SkDrawIter iter(this);
2780 while(iter.next()) {
2781 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2782 }
2783}
2784
2785void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2786 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2787 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002788 if (count <= 0) {
2789 // Nothing to draw
2790 return;
2791 }
2792
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002793 SkPaint realPaint;
2794 init_image_paint(&realPaint, paint);
2795
Michael Ludwiga4b44882019-08-28 14:34:58 -04002796 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2797 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2798 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2799 // or we need it for the autolooper (since it greatly improves image filter perf).
2800 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2801 bool setBoundsValid = count == 1 || needsAutoLooper;
2802 SkRect setBounds = imageSet[0].fDstRect;
2803 if (imageSet[0].fMatrixIndex >= 0) {
2804 // Account for the per-entry transform that is applied prior to the CTM when drawing
2805 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002806 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002807 if (needsAutoLooper) {
2808 for (int i = 1; i < count; ++i) {
2809 SkRect entryBounds = imageSet[i].fDstRect;
2810 if (imageSet[i].fMatrixIndex >= 0) {
2811 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2812 }
2813 setBounds.joinPossiblyEmptyRect(entryBounds);
2814 }
2815 }
2816
2817 // If we happen to have the draw bounds, though, might as well check quickReject().
2818 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2819 SkRect tmp;
2820 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2821 return;
2822 }
2823 }
2824
2825 if (needsAutoLooper) {
2826 SkASSERT(setBoundsValid);
2827 DRAW_BEGIN(realPaint, &setBounds)
2828 while (iter.next()) {
2829 iter.fDevice->drawEdgeAAImageSet(
2830 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2831 }
2832 DRAW_END
2833 } else {
2834 this->predrawNotify();
2835 SkDrawIter iter(this);
2836 while(iter.next()) {
2837 iter.fDevice->drawEdgeAAImageSet(
2838 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2839 }
2840 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002841}
2842
reed@android.com8a1c16f2008-12-17 15:59:43 +00002843//////////////////////////////////////////////////////////////////////////////
2844// These methods are NOT virtual, and therefore must call back into virtual
2845// methods, rather than actually drawing themselves.
2846//////////////////////////////////////////////////////////////////////////////
2847
reed374772b2016-10-05 17:33:02 -07002848void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002849 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002850 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002851 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002852 this->drawPaint(paint);
2853}
2854
2855void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002856 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002857 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2858}
2859
Mike Reed3661bc92017-02-22 13:21:42 -05002860void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002861 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 pts[0].set(x0, y0);
2863 pts[1].set(x1, y1);
2864 this->drawPoints(kLines_PointMode, 2, pts, paint);
2865}
2866
Mike Reed3661bc92017-02-22 13:21:42 -05002867void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002868 if (radius < 0) {
2869 radius = 0;
2870 }
2871
2872 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002873 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002874 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002875}
2876
2877void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2878 const SkPaint& paint) {
2879 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002880 SkRRect rrect;
2881 rrect.setRectXY(r, rx, ry);
2882 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002883 } else {
2884 this->drawRect(r, paint);
2885 }
2886}
2887
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2889 SkScalar sweepAngle, bool useCenter,
2890 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002891 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002892 if (oval.isEmpty() || !sweepAngle) {
2893 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002894 }
bsalomon21af9ca2016-08-25 12:29:23 -07002895 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002896}
2897
reed@android.comf76bacf2009-05-13 14:00:33 +00002898///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002899#ifdef SK_DISABLE_SKPICTURE
2900void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002901
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002902
2903void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2904 const SkPaint* paint) {}
2905#else
Mike Klein88d90712018-01-27 17:30:04 +00002906/**
2907 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2908 * against the playback cost of recursing into the subpicture to get at its actual ops.
2909 *
2910 * For now we pick a conservatively small value, though measurement (and other heuristics like
2911 * the type of ops contained) may justify changing this value.
2912 */
2913#define kMaxPictureOpsToUnrollInsteadOfRef 1
2914
reedd5fa1a42014-08-09 11:08:05 -07002915void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002916 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002917 RETURN_ON_NULL(picture);
2918
reede3b38ce2016-01-08 09:18:44 -08002919 if (matrix && matrix->isIdentity()) {
2920 matrix = nullptr;
2921 }
Mike Klein88d90712018-01-27 17:30:04 +00002922 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2923 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2924 picture->playback(this);
2925 } else {
2926 this->onDrawPicture(picture, matrix, paint);
2927 }
reedd5fa1a42014-08-09 11:08:05 -07002928}
robertphillips9b14f262014-06-04 05:40:44 -07002929
reedd5fa1a42014-08-09 11:08:05 -07002930void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2931 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002932 if (!paint || paint->canComputeFastBounds()) {
2933 SkRect bounds = picture->cullRect();
2934 if (paint) {
2935 paint->computeFastBounds(bounds, &bounds);
2936 }
2937 if (matrix) {
2938 matrix->mapRect(&bounds);
2939 }
2940 if (this->quickReject(bounds)) {
2941 return;
2942 }
2943 }
2944
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002945 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002946 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002947}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002948#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949
reed@android.com8a1c16f2008-12-17 15:59:43 +00002950///////////////////////////////////////////////////////////////////////////////
2951///////////////////////////////////////////////////////////////////////////////
2952
reed3aafe112016-08-18 12:45:34 -07002953SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002954 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955
2956 SkASSERT(canvas);
2957
reed3aafe112016-08-18 12:45:34 -07002958 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002959 fDone = !fImpl->next();
2960}
2961
2962SkCanvas::LayerIter::~LayerIter() {
2963 fImpl->~SkDrawIter();
2964}
2965
2966void SkCanvas::LayerIter::next() {
2967 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002968 if (!fDone) {
2969 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2970 // filters, so its devices will always be able to report the origin exactly.
2971 fDeviceOrigin = fImpl->fDevice->getOrigin();
2972 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002973}
2974
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002975SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002976 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977}
2978
2979const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002980 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002981}
2982
2983const SkPaint& SkCanvas::LayerIter::paint() const {
2984 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002985 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986 paint = &fDefaultPaint;
2987 }
2988 return *paint;
2989}
2990
Mike Reedca37f322018-03-08 13:22:16 -05002991SkIRect SkCanvas::LayerIter::clipBounds() const {
2992 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002993}
2994
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002995int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2996int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002997
2998///////////////////////////////////////////////////////////////////////////////
2999
Brian Osmane8a98632019-04-10 10:26:10 -04003000SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3001SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3002SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3003SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3004
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003005SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3006 const SkRect& dstRect, int matrixIndex, float alpha,
3007 unsigned aaFlags, bool hasClip)
3008 : fImage(std::move(image))
3009 , fSrcRect(srcRect)
3010 , fDstRect(dstRect)
3011 , fMatrixIndex(matrixIndex)
3012 , fAlpha(alpha)
3013 , fAAFlags(aaFlags)
3014 , fHasClip(hasClip) {}
3015
3016SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3017 const SkRect& dstRect, float alpha, unsigned aaFlags)
3018 : fImage(std::move(image))
3019 , fSrcRect(srcRect)
3020 , fDstRect(dstRect)
3021 , fAlpha(alpha)
3022 , fAAFlags(aaFlags) {}
3023
3024///////////////////////////////////////////////////////////////////////////////
3025
Mike Reed5df49342016-11-12 08:06:55 -06003026std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003027 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003028 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003029 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003030 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003031
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003032 SkBitmap bitmap;
3033 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003034 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003035 }
Mike Reed12f77342017-11-08 11:19:52 -05003036
3037 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003038 std::make_unique<SkCanvas>(bitmap, *props) :
3039 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003040}
reedd5fa1a42014-08-09 11:08:05 -07003041
3042///////////////////////////////////////////////////////////////////////////////
3043
Florin Malitaee424ac2016-12-01 12:47:59 -05003044SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003045 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003046
Florin Malita439ace92016-12-02 12:05:41 -05003047SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003048 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003049
Herb Derbyefe39bc2018-05-01 17:06:20 -04003050SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003051 : INHERITED(device) {}
3052
Florin Malitaee424ac2016-12-01 12:47:59 -05003053SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3054 (void)this->INHERITED::getSaveLayerStrategy(rec);
3055 return kNoLayer_SaveLayerStrategy;
3056}
3057
Mike Reed148b7fd2018-12-18 17:38:18 -05003058bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3059 return false;
3060}
3061
Florin Malitaee424ac2016-12-01 12:47:59 -05003062///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003063
reed73603f32016-09-20 08:42:38 -07003064static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3065static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3066static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3067static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3068static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3069static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003070
3071///////////////////////////////////////////////////////////////////////////////////////////////////
3072
3073SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3074 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003075 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003076 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003077 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003078 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003079 clip.setEmpty();
3080 }
3081
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003082 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003083 return handle;
3084 }
3085 return nullptr;
3086}
3087
3088static bool install(SkBitmap* bm, const SkImageInfo& info,
3089 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003090 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003091}
3092
3093SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3094 SkBitmap* bm) {
3095 SkRasterHandleAllocator::Rec rec;
3096 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3097 return nullptr;
3098 }
3099 return rec.fHandle;
3100}
3101
3102std::unique_ptr<SkCanvas>
3103SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3104 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003105 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003106 return nullptr;
3107 }
3108
3109 SkBitmap bm;
3110 Handle hndl;
3111
3112 if (rec) {
3113 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3114 } else {
3115 hndl = alloc->allocBitmap(info, &bm);
3116 }
3117 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3118}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003119
3120///////////////////////////////////////////////////////////////////////////////////////////////////