blob: e08dba39f493cf09f855d1d868e16b1e6eb465c8 [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"
44#include "src/image/SkImage_Base.h"
45#include "src/image/SkSurface_Base.h"
46#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040047
bungemand3ebb482015-08-05 13:57:49 -070048#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000050#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050051#include "include/gpu/GrContext.h"
52#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000053#endif
54
reede3b38ce2016-01-08 09:18:44 -080055#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050056#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080057
Mike Klein1bb7e232019-12-10 08:58:52 -060058// This is a test: static_assert with no message is a c++17 feature,
59// and std::max() is constexpr only since the c++14 stdlib.
60static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000061
Mike Reed139e5e02017-03-08 11:29:33 -050062///////////////////////////////////////////////////////////////////////////////////////////////////
63
reedc83a2972015-07-16 07:40:45 -070064/*
65 * Return true if the drawing this rect would hit every pixels in the canvas.
66 *
67 * Returns false if
68 * - rect does not contain the canvas' bounds
69 * - paint is not fill
70 * - paint would blur or otherwise change the coverage of the rect
71 */
72bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
73 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070074 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
75 (int)kNone_ShaderOverrideOpacity,
76 "need_matching_enums0");
77 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
78 (int)kOpaque_ShaderOverrideOpacity,
79 "need_matching_enums1");
80 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
81 (int)kNotOpaque_ShaderOverrideOpacity,
82 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070083
84 const SkISize size = this->getBaseLayerSize();
85 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050086
87 // if we're clipped at all, we can't overwrite the entire surface
88 {
89 SkBaseDevice* base = this->getDevice();
90 SkBaseDevice* top = this->getTopDevice();
91 if (base != top) {
92 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
93 }
94 if (!base->clipIsWideOpen()) {
95 return false;
96 }
reedc83a2972015-07-16 07:40:45 -070097 }
98
99 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700100 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700101 return false; // conservative
102 }
halcanaryc5769b22016-08-10 07:13:21 -0700103
104 SkRect devRect;
105 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
106 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700107 return false;
108 }
109 }
110
111 if (paint) {
112 SkPaint::Style paintStyle = paint->getStyle();
113 if (!(paintStyle == SkPaint::kFill_Style ||
114 paintStyle == SkPaint::kStrokeAndFill_Style)) {
115 return false;
116 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400117 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700118 return false; // conservative
119 }
120 }
121 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
122}
123
124///////////////////////////////////////////////////////////////////////////////////////////////////
125
reed@google.comda17f752012-08-16 18:27:05 +0000126// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127//#define SK_TRACE_SAVERESTORE
128
129#ifdef SK_TRACE_SAVERESTORE
130 static int gLayerCounter;
131 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
132 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
133
134 static int gRecCounter;
135 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
136 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
137
138 static int gCanvasCounter;
139 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
140 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
141#else
142 #define inc_layer()
143 #define dec_layer()
144 #define inc_rec()
145 #define dec_rec()
146 #define inc_canvas()
147 #define dec_canvas()
148#endif
149
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000150typedef SkTLazy<SkPaint> SkLazyPaint;
151
reedc83a2972015-07-16 07:40:45 -0700152void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000153 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700154 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
155 ? SkSurface::kDiscard_ContentChangeMode
156 : SkSurface::kRetain_ContentChangeMode);
157 }
158}
159
160void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
161 ShaderOverrideOpacity overrideOpacity) {
162 if (fSurfaceBase) {
163 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
164 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
165 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
166 // and therefore we don't care which mode we're in.
167 //
168 if (fSurfaceBase->outstandingImageSnapshot()) {
169 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
170 mode = SkSurface::kDiscard_ContentChangeMode;
171 }
172 }
173 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000174 }
175}
176
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000179/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 The clip/matrix/proc are fields that reflect the top of the save/restore
181 stack. Whenever the canvas changes, it marks a dirty flag, and then before
182 these are used (assuming we're not on a layer) we rebuild these cache
183 values: they reflect the top of the save stack, but translated and clipped
184 by the device's XY offset and bitmap-bounds.
185*/
186struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400187 DeviceCM* fNext;
188 sk_sp<SkBaseDevice> fDevice;
189 SkRasterClip fClip;
190 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
191 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400192 sk_sp<SkImage> fClipImage;
193 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
Florin Malita53f77bd2017-04-28 13:48:37 -0400195 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000196 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700197 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400198 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500199 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700200 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000201 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400202 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400203 {}
reed@google.com4b226022011-01-11 18:32:13 +0000204
mtkleinfeaadee2015-04-08 11:25:48 -0700205 void reset(const SkIRect& bounds) {
206 SkASSERT(!fPaint);
207 SkASSERT(!fNext);
208 SkASSERT(fDevice);
209 fClip.setRect(bounds);
210 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211};
212
Mike Reed148b7fd2018-12-18 17:38:18 -0500213namespace {
214// Encapsulate state needed to restore from saveBehind()
215struct BackImage {
216 sk_sp<SkSpecialImage> fImage;
217 SkIPoint fLoc;
218};
219}
220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221/* This is the record we keep for each save/restore level in the stack.
222 Since a level optionally copies the matrix and/or stack, we have pointers
223 for these fields. If the value is copied for this level, the copy is
224 stored in the ...Storage field, and the pointer points to that. If the
225 value is not copied for this level, we ignore ...Storage, and just point
226 at the corresponding value in the previous level in the stack.
227*/
228class SkCanvas::MCRec {
229public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500230 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 /* If there are any layers in the stack, this points to the top-most
232 one that is at or below this level in the stack (so we know what
233 bitmap/device to draw into from this level. This value is NOT
234 reference counted, since the real owner is either our fLayer field,
235 or a previous one in a lower level.)
236 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500237 DeviceCM* fTopLayer;
238 std::unique_ptr<BackImage> fBackImage;
239 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500240 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500241 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
Mike Reeda1361362017-03-07 09:37:29 -0500243 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700244 fLayer = nullptr;
245 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800246 fMatrix.reset();
247 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700248
reedd9544982014-09-09 18:46:22 -0700249 // don't bother initializing fNext
250 inc_rec();
251 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400252 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700253 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700254 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800255 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 // don't bother initializing fNext
258 inc_rec();
259 }
260 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700261 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 dec_rec();
263 }
mtkleinfeaadee2015-04-08 11:25:48 -0700264
265 void reset(const SkIRect& bounds) {
266 SkASSERT(fLayer);
267 SkASSERT(fDeferredSaveCount == 0);
268
269 fMatrix.reset();
270 fRasterClip.setRect(bounds);
271 fLayer->reset(bounds);
272 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
Mike Reeda1361362017-03-07 09:37:29 -0500275class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276public:
Mike Reeda1361362017-03-07 09:37:29 -0500277 SkDrawIter(SkCanvas* canvas)
278 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
279 {}
reed@google.com4b226022011-01-11 18:32:13 +0000280
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000282 const DeviceCM* rec = fCurrLayer;
283 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400284 fDevice = rec->fDevice.get();
285 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700287 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 return true;
289 }
290 return false;
291 }
reed@google.com4b226022011-01-11 18:32:13 +0000292
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000294
Mike Reed99330ba2017-02-22 11:01:08 -0500295 SkBaseDevice* fDevice;
296
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 const DeviceCM* fCurrLayer;
299 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300};
301
Florin Malita713b8ef2017-04-28 10:57:24 -0400302#define FOR_EACH_TOP_DEVICE( code ) \
303 do { \
304 DeviceCM* layer = fMCRec->fTopLayer; \
305 while (layer) { \
306 SkBaseDevice* device = layer->fDevice.get(); \
307 if (device) { \
308 code; \
309 } \
310 layer = layer->fNext; \
311 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500312 } while (0)
313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314/////////////////////////////////////////////////////////////////////////////
315
reeddbc3cef2015-04-29 12:18:57 -0700316/**
317 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700318 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700319 */
reedd053ce92016-03-22 10:17:23 -0700320static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700321 SkImageFilter* imgf = paint.getImageFilter();
322 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700323 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700324 }
325
reedd053ce92016-03-22 10:17:23 -0700326 SkColorFilter* imgCFPtr;
327 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700328 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700329 }
reedd053ce92016-03-22 10:17:23 -0700330 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700331
332 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700333 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700334 // there is no existing paint colorfilter, so we can just return the imagefilter's
335 return imgCF;
336 }
337
338 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
339 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500340 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700341}
342
senorblanco87e066e2015-10-28 11:23:36 -0700343/**
344 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
345 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
346 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
347 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
348 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
349 * conservative "effective" bounds based on the settings in the paint... with one exception. This
350 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
351 * deliberately ignored.
352 */
353static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
354 const SkRect& rawBounds,
355 SkRect* storage) {
356 SkPaint tmpUnfiltered(paint);
357 tmpUnfiltered.setImageFilter(nullptr);
358 if (tmpUnfiltered.canComputeFastBounds()) {
359 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
360 } else {
361 return rawBounds;
362 }
363}
364
Mike Reed38992392019-07-30 10:48:15 -0400365class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366public:
senorblanco87e066e2015-10-28 11:23:36 -0700367 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
368 // paint. It's used to determine the size of the offscreen layer for filters.
369 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400370 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
371 bool skipLayerForImageFilter = false,
372 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400374 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700376 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
Mike Reed38992392019-07-30 10:48:15 -0400378 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
379 SkASSERT(!fLazyPaint.isValid());
380 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700381 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700382 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700383 fPaint = paint;
384 }
385
386 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700387 /**
388 * We implement ImageFilters for a given draw by creating a layer, then applying the
389 * imagefilter to the pixels of that layer (its backing surface/image), and then
390 * we call restore() to xfer that layer to the main canvas.
391 *
392 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
393 * 2. Generate the src pixels:
394 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
395 * return (fPaint). We then draw the primitive (using srcover) into a cleared
396 * buffer/surface.
397 * 3. Restore the layer created in #1
398 * The imagefilter is passed the buffer/surface from the layer (now filled with the
399 * src pixels of the primitive). It returns a new "filtered" buffer, which we
400 * draw onto the previous layer using the xfermode from the original paint.
401 */
Mike Reed38992392019-07-30 10:48:15 -0400402
403 SkPaint restorePaint;
404 restorePaint.setImageFilter(fPaint->refImageFilter());
405 restorePaint.setBlendMode(fPaint->getBlendMode());
406
senorblanco87e066e2015-10-28 11:23:36 -0700407 SkRect storage;
408 if (rawBounds) {
409 // Make rawBounds include all paint outsets except for those due to image filters.
410 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
411 }
Mike Reed38992392019-07-30 10:48:15 -0400412 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700413 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700414 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000415
Mike Reed38992392019-07-30 10:48:15 -0400416 // Remove the restorePaint fields from our "working" paint
417 SkASSERT(!fLazyPaint.isValid());
418 SkPaint* paint = fLazyPaint.set(origPaint);
419 paint->setImageFilter(nullptr);
420 paint->setBlendMode(SkBlendMode::kSrcOver);
421 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000422 }
423 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000424
Mike Reed38992392019-07-30 10:48:15 -0400425 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700426 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000427 fCanvas->internalRestore();
428 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000429 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000431
reed@google.com4e2b3d32011-04-07 14:18:59 +0000432 const SkPaint& paint() const {
433 SkASSERT(fPaint);
434 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437private:
Mike Reed38992392019-07-30 10:48:15 -0400438 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000439 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400440 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700442 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443};
444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445////////// macros to place around the internal draw calls //////////////////
446
Mike Reed38992392019-07-30 10:48:15 -0400447#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700448 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400449 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
450 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800451
452
Mike Reed38992392019-07-30 10:48:15 -0400453#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000454 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400455 AutoLayerForImageFilter draw(this, paint, true); \
456 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000457
Mike Reed38992392019-07-30 10:48:15 -0400458#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000459 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400460 AutoLayerForImageFilter draw(this, paint, false, bounds); \
461 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000462
Mike Reed38992392019-07-30 10:48:15 -0400463#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700464 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400465 AutoLayerForImageFilter draw(this, paint, false, bounds); \
466 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700467
Mike Reed38992392019-07-30 10:48:15 -0400468#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469
470////////////////////////////////////////////////////////////////////////////
471
msarettfbfa2582016-08-12 08:29:08 -0700472static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
473 if (bounds.isEmpty()) {
474 return SkRect::MakeEmpty();
475 }
476
477 // Expand bounds out by 1 in case we are anti-aliasing. We store the
478 // bounds as floats to enable a faster quick reject implementation.
479 SkRect dst;
480 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
481 return dst;
482}
483
mtkleinfeaadee2015-04-08 11:25:48 -0700484void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
485 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700486 fMCRec->reset(bounds);
487
488 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500489 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400490 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700491 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700492 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700493}
494
Hal Canary363a3f82018-10-04 11:04:48 -0400495void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800496 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497
498 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500499 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500500 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700501 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
reeda499f902015-05-01 09:34:31 -0700503 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
504 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400505 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508
halcanary96fcdcc2015-08-27 07:41:13 -0700509 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000510
reedf92c8662014-08-18 08:02:43 -0700511 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700512 // The root device and the canvas should always have the same pixel geometry
513 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800514 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700515 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500516
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700518 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400519
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500520 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521}
522
reed@google.comcde92112011-07-06 20:00:52 +0000523SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000524 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700525 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000527 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000528
Hal Canary363a3f82018-10-04 11:04:48 -0400529 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000530}
531
reed96a857e2015-01-25 10:33:58 -0800532SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800534 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535{
536 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400537 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500538 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700539}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000540
Hal Canary363a3f82018-10-04 11:04:48 -0400541SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700542 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700543 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700544{
545 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700546
Mike Reed566e53c2017-03-10 10:49:45 -0500547 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400548 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700549}
550
Herb Derbyefe39bc2018-05-01 17:06:20 -0400551SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000552 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700553 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700556
Hal Canary363a3f82018-10-04 11:04:48 -0400557 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700558}
559
reed4a8126e2014-09-22 07:29:03 -0700560SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700561 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700562 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700563{
564 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700565
Mike Reed910ca0f2018-04-25 13:04:05 -0400566 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400567 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700568}
reed29c857d2014-09-21 10:25:07 -0700569
Mike Reed356f7c22017-01-10 11:58:39 -0500570SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
571 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700572 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
573 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500574 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700575{
576 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700577
Mike Reed910ca0f2018-04-25 13:04:05 -0400578 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400579 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580}
581
Mike Reed356f7c22017-01-10 11:58:39 -0500582SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
583
Matt Sarett31f99ce2017-04-11 08:46:01 -0400584#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
585SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
586 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
587 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
588 , fAllocator(nullptr)
589{
590 inc_canvas();
591
592 SkBitmap tmp(bitmap);
593 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400594 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400595 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400596}
597#endif
598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599SkCanvas::~SkCanvas() {
600 // free up the contents of our deque
601 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 this->internalRestore(); // restore the last, since we're going away
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 dec_canvas();
606}
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608///////////////////////////////////////////////////////////////////////////////
609
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000610void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700611 this->onFlush();
612}
613
614void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000615 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000616 if (device) {
617 device->flush();
618 }
619}
620
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500621SkSurface* SkCanvas::getSurface() const {
622 return fSurfaceBase;
623}
624
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000625SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000626 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000627 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
628}
629
senorblancoafc7cce2016-02-02 18:44:15 -0800630SkIRect SkCanvas::getTopLayerBounds() const {
631 SkBaseDevice* d = this->getTopDevice();
632 if (!d) {
633 return SkIRect::MakeEmpty();
634 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500635 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800636}
637
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000638SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000640 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400642 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643}
644
Florin Malita0ed3b642017-01-13 16:56:38 +0000645SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400646 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000647}
648
Mike Reed353196f2017-07-21 11:01:18 -0400649bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000650 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400651 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000652}
653
Mike Reed353196f2017-07-21 11:01:18 -0400654bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
655 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400656}
657
658bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
659 SkPixmap pm;
660 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
661}
662
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000663bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400664 SkPixmap pm;
665 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700666 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000667 }
668 return false;
669}
670
Matt Sarett03dd6d52017-01-23 12:15:09 -0500671bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000672 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 SkBaseDevice* device = this->getDevice();
674 if (!device) {
675 return false;
676 }
677
Matt Sarett03dd6d52017-01-23 12:15:09 -0500678 // This check gives us an early out and prevents generation ID churn on the surface.
679 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
680 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400681 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500682 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000683 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684
Matt Sarett03dd6d52017-01-23 12:15:09 -0500685 // Tell our owning surface to bump its generation ID.
686 const bool completeOverwrite =
687 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700688 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700689
Matt Sarett03dd6d52017-01-23 12:15:09 -0500690 // This can still fail, most notably in the case of a invalid color type or alpha type
691 // conversion. We could pull those checks into this function and avoid the unnecessary
692 // generation ID bump. But then we would be performing those checks twice, since they
693 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400694 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000695}
reed@google.com51df9e32010-12-23 19:29:18 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697//////////////////////////////////////////////////////////////////////////////
698
reed2ff1fce2014-12-11 07:07:37 -0800699void SkCanvas::checkForDeferredSave() {
700 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800701 this->doSave();
702 }
703}
704
reedf0090cb2014-11-26 08:55:51 -0800705int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800706#ifdef SK_DEBUG
707 int count = 0;
708 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
709 for (;;) {
710 const MCRec* rec = (const MCRec*)iter.next();
711 if (!rec) {
712 break;
713 }
714 count += 1 + rec->fDeferredSaveCount;
715 }
716 SkASSERT(count == fSaveCount);
717#endif
718 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800719}
720
721int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800722 fSaveCount += 1;
723 fMCRec->fDeferredSaveCount += 1;
724 return this->getSaveCount() - 1; // return our prev value
725}
726
727void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800728 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700729
730 SkASSERT(fMCRec->fDeferredSaveCount > 0);
731 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800732 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800733}
734
Mike Reed00a97642020-01-25 18:42:23 -0500735int SkCanvas::experimental_saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500736 // TODO: add a virtual for this, and update clients (e.g. chrome)
737 int n = this->save();
Mike Reed46f5c5f2020-02-20 15:42:29 -0500738 this->concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500739 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500740 return n;
741}
742
Mike Reed00a97642020-01-25 18:42:23 -0500743int SkCanvas::experimental_saveCamera(const SkScalar projection[16],
744 const SkScalar camera[16]) {
745 SkM44 proj, cam;
746 proj.setColMajor(projection);
747 cam.setColMajor(camera);
748 return this->experimental_saveCamera(proj, cam);
749}
750
reedf0090cb2014-11-26 08:55:51 -0800751void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800752 if (fMCRec->fDeferredSaveCount > 0) {
753 SkASSERT(fSaveCount > 1);
754 fSaveCount -= 1;
755 fMCRec->fDeferredSaveCount -= 1;
756 } else {
757 // check for underflow
758 if (fMCStack.count() > 1) {
759 this->willRestore();
760 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700761 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800762 this->internalRestore();
763 this->didRestore();
764 }
reedf0090cb2014-11-26 08:55:51 -0800765 }
766}
767
768void SkCanvas::restoreToCount(int count) {
769 // sanity check
770 if (count < 1) {
771 count = 1;
772 }
mtkleinf0f14112014-12-12 08:46:25 -0800773
reedf0090cb2014-11-26 08:55:51 -0800774 int n = this->getSaveCount() - count;
775 for (int i = 0; i < n; ++i) {
776 this->restore();
777 }
778}
779
reed2ff1fce2014-12-11 07:07:37 -0800780void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700782 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000784
Mike Reedc42a1cd2017-02-14 14:25:14 -0500785 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786}
787
reed4960eee2015-12-18 07:09:18 -0800788bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400789 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790}
791
reed4960eee2015-12-18 07:09:18 -0800792bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700793 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400794 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
795 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
796 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
797 // filter does not require a decomposed CTM matrix, the filter space and device space are the
798 // same. When it has been decomposed, we want the original image filter node to process the
799 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
800 // longer the original filter, but has the remainder matrix baked into it, and passing in the
801 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
802 // to the original filter node (barring inflation from consecutive calls to mapRect). While
803 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
804 // passing getDeviceClipBounds() to 'imageFilter' is correct.
805 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
806 // be important to more accurately calculate the clip bounds in the layer space for the original
807 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500808 SkIRect clipBounds = this->getDeviceClipBounds();
809 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000811 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000812
reed96e657d2015-03-10 17:30:07 -0700813 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
814
Robert Phillips12078432018-05-17 11:17:39 -0400815 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
816 // If the image filter DAG affects transparent black then we will need to render
817 // out to the clip bounds
818 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000819 }
Robert Phillips12078432018-05-17 11:17:39 -0400820
821 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700822 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700824 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400825 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400827 inputSaveLayerBounds = clipBounds;
828 }
829
830 if (imageFilter) {
831 // expand the clip bounds by the image filter DAG to include extra content that might
832 // be required by the image filters.
833 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
834 SkImageFilter::kReverse_MapDirection,
835 &inputSaveLayerBounds);
836 }
837
838 SkIRect clippedSaveLayerBounds;
839 if (bounds) {
840 // For better or for worse, user bounds currently act as a hard clip on the layer's
841 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
842 clippedSaveLayerBounds = inputSaveLayerBounds;
843 } else {
844 // If there are no user bounds, we don't want to artificially restrict the resulting
845 // layer bounds, so allow the expanded clip bounds free reign.
846 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800848
849 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400850 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800851 if (BoundsAffectsClip(saveLayerFlags)) {
852 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
853 fMCRec->fRasterClip.setEmpty();
854 fDeviceClipBounds.setEmpty();
855 }
856 return false;
857 }
Robert Phillips12078432018-05-17 11:17:39 -0400858 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859
reed4960eee2015-12-18 07:09:18 -0800860 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700861 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400862 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
863 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000864 }
865
866 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400867 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000868 }
Robert Phillips12078432018-05-17 11:17:39 -0400869
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870 return true;
871}
872
reed4960eee2015-12-18 07:09:18 -0800873int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
874 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000875}
876
Cary Clarke041e312018-03-06 13:00:52 -0500877int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700878 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400879 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
880 // no need for the layer (or any of the draws until the matching restore()
881 this->save();
882 this->clipRect({0,0,0,0});
883 } else {
884 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
885 fSaveCount += 1;
886 this->internalSaveLayer(rec, strategy);
887 }
reed4960eee2015-12-18 07:09:18 -0800888 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800889}
890
Mike Reed148b7fd2018-12-18 17:38:18 -0500891int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
892 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
893 // Assuming clips never expand, if the request bounds is outside of the current clip
894 // there is no need to copy/restore the area, so just devolve back to a regular save.
895 this->save();
896 } else {
897 bool doTheWork = this->onDoSaveBehind(bounds);
898 fSaveCount += 1;
899 this->internalSave();
900 if (doTheWork) {
901 this->internalSaveBehind(bounds);
902 }
903 }
904 return this->getSaveCount() - 1;
905}
906
reeda2217ef2016-07-20 06:04:34 -0700907void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500908 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500909 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400910 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
911 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400912 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500913 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
914 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400915 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400916 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
917 // This means that we only have to copy a dst-sized block of pixels out of src and translate
918 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400919 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
920 dstOrigin.y() - src->getOrigin().y(),
921 dst->width(), dst->height());
922 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400923 return;
924 }
925
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400926 auto special = src->snapSpecial(snapBounds);
927 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400928 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
929 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400930 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
931 }
932 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400933 }
934
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400935 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
936 // by the backdrop filter.
937 SkMatrix toRoot, layerMatrix;
938 SkSize scale;
939 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
940 toRoot = SkMatrix::I();
941 layerMatrix = ctm;
942 } else if (ctm.decomposeScale(&scale, &toRoot)) {
943 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
944 } else {
945 // Perspective, for now, do no scaling of the layer itself.
946 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
947 // the matrix, e.g. based on the midpoint of the near/far planes?
948 toRoot = ctm;
949 layerMatrix = SkMatrix::I();
950 }
951
952 // We have to map the dst bounds from the root space into the layer space where filtering will
953 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
954 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
955 // is a safe, conservative estimate.
956 SkMatrix fromRoot;
957 if (!toRoot.invert(&fromRoot)) {
958 return;
959 }
960
961 // This represents what the backdrop filter needs to produce in the layer space, and is sized
962 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
963 SkIRect layerTargetBounds = fromRoot.mapRect(
964 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
965 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
966 // require some extra input pixels.
967 SkIRect layerInputBounds = filter->filterBounds(
968 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
969 &layerTargetBounds);
970
971 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400972 // be the conservative contents required to fill a layerInputBounds-sized surface with the
973 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400974 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
975 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
976 if (!backdropBounds.intersect(srcDevRect)) {
977 return;
978 }
979
980 auto special = src->snapSpecial(backdropBounds);
981 if (!special) {
982 return;
983 }
984
985 SkColorType colorType = src->imageInfo().colorType();
986 if (colorType == kUnknown_SkColorType) {
987 colorType = kRGBA_8888_SkColorType;
988 }
989 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400990
991 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400992 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400993 // Drawing the temporary and final filtered image requires a higher filter quality if the
994 // 'toRoot' transformation is not identity, in order to minimize the impact on already
995 // rendered edges/content.
996 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
997 p.setFilterQuality(kHigh_SkFilterQuality);
998
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400999 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1000 // and stored in a temporary surface, which is then used as the input to the actual filter.
1001 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1002 if (!tmpSurface) {
1003 return;
1004 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001005
1006 auto tmpCanvas = tmpSurface->getCanvas();
1007 tmpCanvas->clear(SK_ColorTRANSPARENT);
1008 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1009 // space, then maps from root space into the layer space, then maps it so the input layer's
1010 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1011 // performed on backdropBounds.
1012 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1013 tmpCanvas->concat(fromRoot);
1014 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001015
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001016 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1017 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1018 special = tmpSurface->makeImageSnapshot();
1019 } else {
1020 // Since there is no extra transform that was done, update the input bounds to reflect
1021 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1022 // was equal to backdropBounds before it was made relative to the src device and cropped.
1023 // When we use the original snapped image directly, just map the update backdrop bounds
1024 // back into the shared layer space
1025 layerInputBounds = backdropBounds;
1026 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001027
1028 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1029 // draw will be 1-1 so there is no need to increase filter quality.
1030 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001031 }
1032
1033 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1034 // layer space. This has to further offset everything so that filter evaluation thinks the
1035 // source image's top left corner is (0, 0).
1036 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1037 // this can be simplified.
1038 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1039 SkMatrix filterCTM = layerMatrix;
1040 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1041 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1042
1043 SkIPoint offset;
1044 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001045 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001046 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1047 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1048 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1049 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050 offset += layerInputBounds.topLeft();
1051
1052 // Manually setting the device's CTM requires accounting for the device's origin.
1053 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001054 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001055 // a global CTM instead of a device CTM.
1056 SkMatrix dstCTM = toRoot;
1057 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001058 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001059
1060 // And because devices don't have a special-image draw function that supports arbitrary
1061 // matrices, we are abusing the asImage() functionality here...
1062 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001063 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001064 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001065 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001066 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1067 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001068 }
robertphillips7354a4b2015-12-16 05:08:27 -08001069}
reed70ee31b2015-12-10 13:44:45 -08001070
Mike Kleine083f7c2018-02-07 12:54:27 -05001071static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001072 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001073 if (prev.bytesPerPixel() <= 4 &&
1074 prev.colorType() != kRGBA_8888_SkColorType &&
1075 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001076 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1077 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1078 ct = kN32_SkColorType;
1079 }
1080 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001081}
1082
reed4960eee2015-12-18 07:09:18 -08001083void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001084 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001085 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001086 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1087
Michael Ludwigeced98b2020-03-03 10:39:41 -05001088 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1089 // saveLayer ignores mask filters, so force it to null
1090 if (paint.get() && paint->getMaskFilter()) {
1091 paint.writable()->setMaskFilter(nullptr);
1092 }
1093
Mike Reed5532c2a2019-02-23 12:00:32 -05001094 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1095 // regardless of any hint-rect from the caller. skbug.com/8783
1096 if (rec.fBackdrop) {
1097 bounds = nullptr;
1098 }
1099
Michael Ludwigeced98b2020-03-03 10:39:41 -05001100 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001101 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001102 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001103
reed8c30a812016-04-20 16:36:51 -07001104 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001105 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1106 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1107 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001108 *
1109 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001110 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1111 * if necessary.
1112 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1113 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001114 * 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 -04001115 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001116 * of the original imagefilter, and draw that (via drawSprite)
1117 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1118 *
1119 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1120 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1121 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001122 if (imageFilter) {
1123 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001124 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1125 &modifiedCTM);
1126 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001127 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001128 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001129 modifiedRec = fMCRec;
1130 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001131 imageFilter = modifiedFilter.get();
1132 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001133 }
1134 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1135 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001136 }
reed8c30a812016-04-20 16:36:51 -07001137
junov@chromium.orga907ac32012-02-24 21:54:07 +00001138 // do this before we create the layer. We don't call the public save() since
1139 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001140 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001141
junov@chromium.orga907ac32012-02-24 21:54:07 +00001142 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001143 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001144 if (modifiedRec) {
1145 // In this case there will be no layer in which to stash the matrix so we need to
1146 // revert the prior MCRec to its earlier state.
1147 modifiedRec->fMatrix = stashedMatrix;
1148 }
reed2ff1fce2014-12-11 07:07:37 -08001149 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 }
1151
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001152 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1153 // the clipRectBounds() call above?
1154 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001155 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001156 }
1157
reed8dc0ccb2015-03-20 06:32:52 -07001158 SkPixelGeometry geo = fProps.pixelGeometry();
1159 if (paint) {
reed76033be2015-03-14 10:54:31 -07001160 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001161 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001162 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001163 }
1164 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165
robertphillips5139e502016-07-19 05:10:40 -07001166 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001167 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001168 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001169 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001170 }
reedb2db8982014-11-13 12:41:02 -08001171
Mike Kleine083f7c2018-02-07 12:54:27 -05001172 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001173 if (rec.fSaveLayerFlags & kF16ColorType) {
1174 info = info.makeColorType(kRGBA_F16_SkColorType);
1175 }
reed129ed1c2016-02-22 06:42:31 -08001176
Hal Canary704cd322016-11-07 14:13:52 -05001177 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001178 {
Florin Malita4571e492019-07-16 10:25:58 -04001179 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001180 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001181 const bool trackCoverage =
1182 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001183 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001184 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001185 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001186 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1187 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001188 return;
reed61f501f2015-04-29 08:34:00 -07001189 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001190 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001191 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192
Mike Reedb43a3e02017-02-11 10:18:58 -05001193 // only have a "next" if this new layer doesn't affect the clip (rare)
1194 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 fMCRec->fLayer = layer;
1196 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001197
Mike Reedc61abee2017-02-28 17:45:27 -05001198 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001199 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001200 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001201 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001202
Mike Reedc42a1cd2017-02-14 14:25:14 -05001203 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1204
1205 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1206 if (layer->fNext) {
1207 // need to punch a hole in the previous device, so we don't draw there, given that
1208 // the new top-layer will allow drawing to happen "below" it.
1209 SkRegion hole(ir);
1210 do {
1211 layer = layer->fNext;
1212 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1213 } while (layer->fNext);
1214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215}
1216
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001217int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001218 if (0xFF == alpha) {
1219 return this->saveLayer(bounds, nullptr);
1220 } else {
1221 SkPaint tmpPaint;
1222 tmpPaint.setAlpha(alpha);
1223 return this->saveLayer(bounds, &tmpPaint);
1224 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001225}
1226
Mike Reed148b7fd2018-12-18 17:38:18 -05001227void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001228 SkBaseDevice* device = this->getTopDevice();
1229 if (nullptr == device) { // Do we still need this check???
1230 return;
1231 }
1232
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001233 // Map the local bounds into the top device's coordinate space (this is not
1234 // necessarily the full global CTM transform).
1235 SkIRect devBounds;
1236 if (localBounds) {
1237 SkRect tmp;
1238 device->localToDevice().mapRect(&tmp, *localBounds);
1239 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1240 devBounds.setEmpty();
1241 }
1242 } else {
1243 devBounds = device->devClipBounds();
1244 }
1245 if (devBounds.isEmpty()) {
1246 return;
1247 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001248
Michael Ludwigac352122019-08-28 21:03:05 +00001249 // This is getting the special image from the current device, which is then drawn into (both by
1250 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1251 // own device, we need to explicitly copy the back image contents so that its original content
1252 // is available when we splat it back later during restore.
1253 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001254 if (!backImage) {
1255 return;
1256 }
1257
1258 // we really need the save, so we can wack the fMCRec
1259 this->checkForDeferredSave();
1260
1261 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1262
1263 SkPaint paint;
1264 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001265 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001266}
1267
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268void SkCanvas::internalRestore() {
1269 SkASSERT(fMCStack.count() != 0);
1270
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001271 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 DeviceCM* layer = fMCRec->fLayer; // may be null
1273 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001274 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275
Mike Reed148b7fd2018-12-18 17:38:18 -05001276 // move this out before we do the actual restore
1277 auto backImage = std::move(fMCRec->fBackImage);
1278
Mike Reedb18e74d2020-01-16 13:58:22 -05001279 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1280 fCameraStack.pop_back();
1281 }
1282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 // now do the normal restore()
1284 fMCRec->~MCRec(); // balanced in save()
1285 fMCStack.pop_back();
1286 fMCRec = (MCRec*)fMCStack.back();
1287
Mike Reedc42a1cd2017-02-14 14:25:14 -05001288 if (fMCRec) {
1289 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1290 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001291
Mike Reed148b7fd2018-12-18 17:38:18 -05001292 if (backImage) {
1293 SkPaint paint;
1294 paint.setBlendMode(SkBlendMode::kDstOver);
1295 const int x = backImage->fLoc.x();
1296 const int y = backImage->fLoc.y();
1297 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1298 nullptr, SkMatrix::I());
1299 }
1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1302 since if we're being recorded, we don't want to record this (the
1303 recorder will have already recorded the restore).
1304 */
bsalomon49f085d2014-09-05 13:34:00 -07001305 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001306 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001307 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001308 // At this point, 'layer' has been removed from the device stack, so the devices that
1309 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1310 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001311 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001312 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001313 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001314 delete layer;
reedb679ca82015-04-07 04:40:48 -07001315 } else {
1316 // we're at the root
reeda499f902015-05-01 09:34:31 -07001317 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001318 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001319 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001321 }
msarettfbfa2582016-08-12 08:29:08 -07001322
1323 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001324 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001325 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
reede8f30622016-03-23 18:59:25 -07001329sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001330 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001331 props = &fProps;
1332 }
1333 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001334}
1335
reede8f30622016-03-23 18:59:25 -07001336sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001337 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001338 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001339}
1340
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001341SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001342 return this->onImageInfo();
1343}
1344
1345SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001346 SkBaseDevice* dev = this->getDevice();
1347 if (dev) {
1348 return dev->imageInfo();
1349 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001350 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001351 }
1352}
1353
brianosman898235c2016-04-06 07:38:23 -07001354bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001355 return this->onGetProps(props);
1356}
1357
1358bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001359 SkBaseDevice* dev = this->getDevice();
1360 if (dev) {
1361 if (props) {
1362 *props = fProps;
1363 }
1364 return true;
1365 } else {
1366 return false;
1367 }
1368}
1369
reed6ceeebd2016-03-09 14:26:26 -08001370bool SkCanvas::peekPixels(SkPixmap* pmap) {
1371 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001372}
1373
reed884e97c2015-05-26 11:31:54 -07001374bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001376 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377}
1378
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001379void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001380 SkPixmap pmap;
1381 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001382 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001383 }
1384 if (info) {
1385 *info = pmap.info();
1386 }
1387 if (rowBytes) {
1388 *rowBytes = pmap.rowBytes();
1389 }
1390 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001391 // If the caller requested the origin, they presumably are expecting the returned pixels to
1392 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1393 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1394 // instead of an origin, just don't expose the pixels in that case. Note that this means
1395 // that layers with complex coordinate spaces can still report their pixels if the caller
1396 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1397 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1398 *origin = this->getTopDevice()->getOrigin();
1399 } else {
1400 return nullptr;
1401 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001402 }
reed884e97c2015-05-26 11:31:54 -07001403 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001404}
1405
reed884e97c2015-05-26 11:31:54 -07001406bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001407 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001408 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001409}
1410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
Mike Reed8bcd1282019-03-13 16:51:54 -04001413// In our current design/features, we should never have a layer (src) in a different colorspace
1414// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1415// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1416// colorspace.
1417static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1418 SkASSERT(src == dst);
1419}
1420
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001421void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001422 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001424 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 paint = &tmp;
1426 }
reed@google.com4b226022011-01-11 18:32:13 +00001427
Mike Reed38992392019-07-30 10:48:15 -04001428 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001431 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001432 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1433 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001434 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001435 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001436 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1437 // so it should always be possible to use the relative origin. Once drawDevice() and
1438 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1439 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001440 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001441 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1442 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001443 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1444 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001445 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1446 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001447 }
reed@google.com76dd2772012-01-05 21:15:07 +00001448 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001449 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 }
reeda2217ef2016-07-20 06:04:34 -07001452
Mike Reed38992392019-07-30 10:48:15 -04001453 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454}
1455
reed32704672015-12-16 08:27:10 -08001456/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001457
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001458void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001459 if (dx || dy) {
1460 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001461 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001462
reedfe69b502016-09-12 06:31:48 -07001463 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001464 // However, if either is non-finite, we might still complicate the matrix type,
1465 // so we still have to compute this.
1466 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001467
Mike Reedc42a1cd2017-02-14 14:25:14 -05001468 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001469
reedfe69b502016-09-12 06:31:48 -07001470 this->didTranslate(dx,dy);
1471 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001475 if (sx != 1 || sy != 1) {
1476 this->checkForDeferredSave();
1477 fMCRec->fMatrix.preScale(sx, sy);
1478
1479 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1480 // but pre-scaling by a non-finite does change it, so we have to recompute.
1481 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1482
1483 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1484
1485 this->didScale(sx, sy);
1486 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487}
1488
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001489void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001490 SkMatrix m;
1491 m.setRotate(degrees);
1492 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
bungeman7438bfc2016-07-12 15:01:19 -07001495void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1496 SkMatrix m;
1497 m.setRotate(degrees, px, py);
1498 this->concat(m);
1499}
1500
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001501void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001502 SkMatrix m;
1503 m.setSkew(sx, sy);
1504 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505}
1506
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001507void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001508 if (matrix.isIdentity()) {
1509 return;
1510 }
1511
reed2ff1fce2014-12-11 07:07:37 -08001512 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001513 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001514
msarett9637ea92016-08-18 14:03:30 -07001515 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001516
Mike Reed7627fa52017-02-08 10:07:53 -05001517 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001518
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001519 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001520}
1521
Mike Reed46f5c5f2020-02-20 15:42:29 -05001522void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001523 this->checkForDeferredSave();
1524
Mike Reed46f5c5f2020-02-20 15:42:29 -05001525 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001526
1527 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1528
1529 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1530
Mike Reed46f5c5f2020-02-20 15:42:29 -05001531 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001532}
Mike Reed064c7f92020-01-08 17:33:04 -05001533
Mike Reed46f5c5f2020-02-20 15:42:29 -05001534void SkCanvas::concat44(const SkM44& m) {
1535 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001536}
1537
reed8c30a812016-04-20 16:36:51 -07001538void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001539 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001540 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001541
Mike Reedc42a1cd2017-02-14 14:25:14 -05001542 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001543}
1544
1545void SkCanvas::setMatrix(const SkMatrix& matrix) {
1546 this->checkForDeferredSave();
1547 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001548 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549}
1550
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001552 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553}
1554
1555//////////////////////////////////////////////////////////////////////////////
1556
Mike Reedc1f77742016-12-09 09:00:50 -05001557void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001558 if (!rect.isFinite()) {
1559 return;
1560 }
reed2ff1fce2014-12-11 07:07:37 -08001561 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1563 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001564}
1565
Mike Reedc1f77742016-12-09 09:00:50 -05001566void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001567 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001568
Mike Reed7627fa52017-02-08 10:07:53 -05001569 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001570
reedc64eff52015-11-21 12:39:45 -08001571 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001572 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1573 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001574 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001577void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1578 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001579 if (fClipRestrictionRect.isEmpty()) {
1580 // we notify the device, but we *dont* resolve deferred saves (since we're just
1581 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001582 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001583 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001584 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001585 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001586 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001587 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001588 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1589 }
1590}
1591
Mike Reedc1f77742016-12-09 09:00:50 -05001592void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001593 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001595 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1597 } else {
1598 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001599 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001601
Mike Reedc1f77742016-12-09 09:00:50 -05001602void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001604
Brian Salomona3b45d42016-10-03 11:36:16 -04001605 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001606
Mike Reed7627fa52017-02-08 10:07:53 -05001607 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001608
Mike Reed20800c82017-11-15 16:09:04 -05001609 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1610 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001611 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001612}
1613
Mike Reedc1f77742016-12-09 09:00:50 -05001614void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001615 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001617
1618 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1619 SkRect r;
1620 if (path.isRect(&r)) {
1621 this->onClipRect(r, op, edgeStyle);
1622 return;
1623 }
1624 SkRRect rrect;
1625 if (path.isOval(&r)) {
1626 rrect.setOval(r);
1627 this->onClipRRect(rrect, op, edgeStyle);
1628 return;
1629 }
1630 if (path.isRRect(&rrect)) {
1631 this->onClipRRect(rrect, op, edgeStyle);
1632 return;
1633 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 }
robertphillips39f05382015-11-24 09:30:12 -08001635
1636 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637}
1638
Mike Reedc1f77742016-12-09 09:00:50 -05001639void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001640 AutoValidateClip avc(this);
1641
Brian Salomona3b45d42016-10-03 11:36:16 -04001642 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001643
Mike Reed7627fa52017-02-08 10:07:53 -05001644 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645
Brian Salomona3b45d42016-10-03 11:36:16 -04001646 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001647 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001648 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001649 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650}
1651
Mike Reed121c2af2020-03-10 14:02:56 -04001652void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1653 if (sh) {
Mike Reedbf355122020-03-10 17:10:04 -04001654 if (sh->isOpaque()) {
1655 if (op == SkClipOp::kIntersect) {
1656 // we don't occlude anything, so skip this call
1657 } else {
1658 SkASSERT(op == SkClipOp::kDifference);
1659 // we occlude everything, so set the clip to empty
1660 this->clipRect({0,0,0,0});
1661 }
1662 } else {
1663 this->onClipShader(std::move(sh), op);
1664 }
Mike Reed121c2af2020-03-10 14:02:56 -04001665 }
1666}
1667
1668void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1669 AutoValidateClip avc(this);
1670
1671 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1672
1673 // we don't know how to mutate our conservative bounds, so we don't
1674}
1675
Mike Reedc1f77742016-12-09 09:00:50 -05001676void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001677 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001678 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679}
1680
Mike Reedc1f77742016-12-09 09:00:50 -05001681void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001682 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001683
reed@google.com5c3d1472011-02-22 19:12:23 +00001684 AutoValidateClip avc(this);
1685
Mike Reed20800c82017-11-15 16:09:04 -05001686 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001687 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688}
1689
reed@google.com819c9212011-02-23 18:56:55 +00001690#ifdef SK_DEBUG
1691void SkCanvas::validateClip() const {
1692 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001693 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001694 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001695 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001696 return;
1697 }
reed@google.com819c9212011-02-23 18:56:55 +00001698}
1699#endif
1700
Mike Reeda1361362017-03-07 09:37:29 -05001701bool SkCanvas::androidFramework_isClipAA() const {
1702 bool containsAA = false;
1703
1704 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1705
1706 return containsAA;
1707}
1708
1709class RgnAccumulator {
1710 SkRegion* fRgn;
1711public:
1712 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1713 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1714 SkIPoint origin = device->getOrigin();
1715 if (origin.x() | origin.y()) {
1716 rgn->translate(origin.x(), origin.y());
1717 }
1718 fRgn->op(*rgn, SkRegion::kUnion_Op);
1719 }
1720};
1721
1722void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1723 RgnAccumulator accum(rgn);
1724 SkRegion tmp;
1725
1726 rgn->setEmpty();
1727 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001728}
1729
reed@google.com5c3d1472011-02-22 19:12:23 +00001730///////////////////////////////////////////////////////////////////////////////
1731
reed@google.com754de5f2014-02-24 19:38:20 +00001732bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001733 return fMCRec->fRasterClip.isEmpty();
1734
1735 // TODO: should we only use the conservative answer in a recording canvas?
1736#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001737 SkBaseDevice* dev = this->getTopDevice();
1738 // if no device we return true
1739 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001740#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001741}
1742
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001743bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001744 SkBaseDevice* dev = this->getTopDevice();
1745 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001746 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001747}
1748
msarettfbfa2582016-08-12 08:29:08 -07001749static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1750#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1751 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1752 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1753 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1754 return 0xF != _mm_movemask_ps(mask);
1755#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1756 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1757 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1758 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1759 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1760#else
1761 SkRect devRectAsRect;
1762 SkRect devClipAsRect;
1763 devRect.store(&devRectAsRect.fLeft);
1764 devClip.store(&devClipAsRect.fLeft);
1765 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1766#endif
1767}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001768
msarettfbfa2582016-08-12 08:29:08 -07001769// It's important for this function to not be inlined. Otherwise the compiler will share code
1770// between the fast path and the slow path, resulting in two slow paths.
1771static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1772 const SkMatrix& matrix) {
1773 SkRect deviceRect;
1774 matrix.mapRect(&deviceRect, src);
1775 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1776}
1777
1778bool SkCanvas::quickReject(const SkRect& src) const {
1779#ifdef SK_DEBUG
1780 // Verify that fDeviceClipBounds are set properly.
1781 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001782 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001783 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001784 } else {
msarettfbfa2582016-08-12 08:29:08 -07001785 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 }
msarettfbfa2582016-08-12 08:29:08 -07001787
msarett9637ea92016-08-18 14:03:30 -07001788 // Verify that fIsScaleTranslate is set properly.
1789 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001790#endif
1791
msarett9637ea92016-08-18 14:03:30 -07001792 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001793 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1794 }
1795
1796 // We inline the implementation of mapScaleTranslate() for the fast path.
1797 float sx = fMCRec->fMatrix.getScaleX();
1798 float sy = fMCRec->fMatrix.getScaleY();
1799 float tx = fMCRec->fMatrix.getTranslateX();
1800 float ty = fMCRec->fMatrix.getTranslateY();
1801 Sk4f scale(sx, sy, sx, sy);
1802 Sk4f trans(tx, ty, tx, ty);
1803
1804 // Apply matrix.
1805 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1806
1807 // Make sure left < right, top < bottom.
1808 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1809 Sk4f min = Sk4f::Min(ltrb, rblt);
1810 Sk4f max = Sk4f::Max(ltrb, rblt);
1811 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1812 // ARM this sequence generates the fastest (a single instruction).
1813 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1814
1815 // Check if the device rect is NaN or outside the clip.
1816 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817}
1818
reed@google.com3b3e8952012-08-16 20:53:31 +00001819bool SkCanvas::quickReject(const SkPath& path) const {
1820 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821}
1822
Mike Klein83c8dd92017-11-28 17:08:45 -05001823SkRect SkCanvas::getLocalClipBounds() const {
1824 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001825 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001826 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 }
1828
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001829 SkMatrix inverse;
1830 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001831 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001832 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001833 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834
Mike Reed42e8c532017-01-23 14:09:13 -05001835 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001836 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001837 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001838
Mike Reedb57b9312018-04-23 12:12:54 -04001839 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001840 inverse.mapRect(&bounds, r);
1841 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842}
1843
Mike Klein83c8dd92017-11-28 17:08:45 -05001844SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001845 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001846}
1847
Mike Reedb18e74d2020-01-16 13:58:22 -05001848///////////////////////////////////////////////////////////////////////
1849
1850SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1851 : fMCRec(owner)
1852 , fCamera(camera)
1853{
1854 // assumes the mcrec has already been concatenated with the camera
1855 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1856 fInvPostCamera.setIdentity();
1857 }
1858}
1859
Mike Reed403c8072020-01-08 10:40:39 -05001860SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001861 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862}
1863
Mike Reed46f5c5f2020-02-20 15:42:29 -05001864SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001865 return fMCRec->fMatrix;
1866}
1867
Mike Reedc43f2a02020-01-16 14:54:34 -05001868SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001869 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001870 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001871 } else {
1872 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001873 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001874 }
1875}
1876
Mike Reedc43f2a02020-01-16 14:54:34 -05001877SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001878 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001879 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001880 } else {
1881 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001882 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001883 }
1884}
1885
Mike Reed46f5c5f2020-02-20 15:42:29 -05001886void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1887 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001888}
1889
1890void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1891 this->experimental_getLocalToWorld().getColMajor(colMajor);
1892}
1893
1894void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1895 this->experimental_getLocalToCamera().getColMajor(colMajor);
1896}
1897
Brian Osman11052242016-10-27 14:47:55 -04001898GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001899 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001900 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001901}
1902
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001903GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001904 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001905 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001906}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001907
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001908void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1909 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001910 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001911 if (outer.isEmpty()) {
1912 return;
1913 }
1914 if (inner.isEmpty()) {
1915 this->drawRRect(outer, paint);
1916 return;
1917 }
1918
1919 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001920 // be able to return ...
1921 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001922 //
1923 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001924 if (!outer.getBounds().contains(inner.getBounds())) {
1925 return;
1926 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001927
1928 this->onDrawDRRect(outer, inner, paint);
1929}
1930
reed41af9662015-01-05 07:49:08 -08001931void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001932 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001933 this->onDrawPaint(paint);
1934}
1935
1936void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001937 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001938 // To avoid redundant logic in our culling code and various backends, we always sort rects
1939 // before passing them along.
1940 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001941}
1942
Mike Reedd5674082019-04-19 15:00:47 -04001943void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1944 TRACE_EVENT0("skia", TRACE_FUNC);
1945 this->onDrawBehind(paint);
1946}
1947
msarettdca352e2016-08-26 06:37:45 -07001948void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001949 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001950 if (region.isEmpty()) {
1951 return;
1952 }
1953
1954 if (region.isRect()) {
1955 return this->drawIRect(region.getBounds(), paint);
1956 }
1957
1958 this->onDrawRegion(region, paint);
1959}
1960
reed41af9662015-01-05 07:49:08 -08001961void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001962 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001963 // To avoid redundant logic in our culling code and various backends, we always sort rects
1964 // before passing them along.
1965 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001966}
1967
1968void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001969 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001970 this->onDrawRRect(rrect, paint);
1971}
1972
1973void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001974 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001975 this->onDrawPoints(mode, count, pts, paint);
1976}
1977
Mike Reede88a1cb2017-03-17 09:50:46 -04001978void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1979 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001980 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001981}
1982
1983void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001984 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001985 RETURN_ON_NULL(vertices);
Mike Reed5caf9352020-03-02 14:57:09 -05001986 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1987 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
1988 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001989}
1990
1991void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001992 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001993 this->onDrawPath(path, paint);
1994}
1995
reeda85d4d02015-05-06 12:56:48 -07001996void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001997 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001998 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001999 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08002000}
2001
Mike Reedc4e31092018-01-30 11:15:27 -05002002// Returns true if the rect can be "filled" : non-empty and finite
2003static bool fillable(const SkRect& r) {
2004 SkScalar w = r.width();
2005 SkScalar h = r.height();
2006 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
2007}
2008
reede47829b2015-08-06 10:02:53 -07002009void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2010 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002011 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002012 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002013 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002014 return;
2015 }
2016 this->onDrawImageRect(image, &src, dst, paint, constraint);
2017}
reed41af9662015-01-05 07:49:08 -08002018
reed84984ef2015-07-17 07:09:43 -07002019void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2020 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002021 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002022 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002023}
2024
Brian Salomonf08002c2018-10-26 16:15:46 -04002025void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002026 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002027 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002028 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002029}
reede47829b2015-08-06 10:02:53 -07002030
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002031namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002032class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002033public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002034 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2035 if (!origPaint) {
2036 return;
2037 }
2038 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2039 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2040 }
2041 if (origPaint->getMaskFilter()) {
2042 fPaint.writable()->setMaskFilter(nullptr);
2043 }
2044 if (origPaint->isAntiAlias()) {
2045 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002046 }
2047 }
2048
2049 const SkPaint* get() const {
2050 return fPaint;
2051 }
2052
2053private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002054 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002055};
2056} // namespace
2057
reed4c21dc52015-06-25 12:32:03 -07002058void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2059 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002060 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002061 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002062 if (dst.isEmpty()) {
2063 return;
2064 }
msarett552bca92016-08-03 06:53:26 -07002065 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002066 LatticePaint latticePaint(paint);
2067 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002068 } else {
reede47829b2015-08-06 10:02:53 -07002069 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002070 }
reed4c21dc52015-06-25 12:32:03 -07002071}
2072
msarett16882062016-08-16 09:31:08 -07002073void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2074 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002075 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002076 RETURN_ON_NULL(image);
2077 if (dst.isEmpty()) {
2078 return;
2079 }
msarett71df2d72016-09-30 12:41:42 -07002080
2081 SkIRect bounds;
2082 Lattice latticePlusBounds = lattice;
2083 if (!latticePlusBounds.fBounds) {
2084 bounds = SkIRect::MakeWH(image->width(), image->height());
2085 latticePlusBounds.fBounds = &bounds;
2086 }
2087
2088 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002089 LatticePaint latticePaint(paint);
2090 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002091 } else {
2092 this->drawImageRect(image, dst, paint);
2093 }
2094}
2095
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002096static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) {
reed4c21dc52015-06-25 12:32:03 -07002097 if (bitmap.drawsNothing()) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002098 return nullptr;
tomhudson2df6fd62015-04-09 09:20:19 -07002099 }
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002100 return SkImage::MakeFromBitmap(bitmap);
2101}
2102
2103void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
2104 this->drawImage(bitmap_as_image(bitmap), dx, dy, paint);
reed41af9662015-01-05 07:49:08 -08002105}
2106
reede47829b2015-08-06 10:02:53 -07002107void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002108 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwigb0cee9b2020-03-12 10:52:15 -04002109 this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002110}
2111
reed84984ef2015-07-17 07:09:43 -07002112void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2113 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002114 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002115}
2116
reede47829b2015-08-06 10:02:53 -07002117void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2118 SrcRectConstraint constraint) {
2119 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2120 constraint);
2121}
reede47829b2015-08-06 10:02:53 -07002122
reed71c3c762015-06-24 10:29:17 -07002123void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002124 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002125 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002126 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002127 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002128 if (count <= 0) {
2129 return;
2130 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002131 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002132 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002133 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002134}
2135
reedf70b5312016-03-04 16:36:20 -08002136void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002137 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002138 if (key) {
2139 this->onDrawAnnotation(rect, key, value);
2140 }
2141}
2142
reede47829b2015-08-06 10:02:53 -07002143void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2144 const SkPaint* paint, SrcRectConstraint constraint) {
2145 if (src) {
2146 this->drawImageRect(image, *src, dst, paint, constraint);
2147 } else {
2148 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2149 dst, paint, constraint);
2150 }
2151}
2152void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2153 const SkPaint* paint, SrcRectConstraint constraint) {
2154 if (src) {
2155 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2156 } else {
2157 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2158 dst, paint, constraint);
2159 }
2160}
2161
Mike Reed4204da22017-05-17 08:53:36 -04002162void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002163 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002164 this->onDrawShadowRec(path, rec);
2165}
2166
2167void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2168 SkPaint paint;
2169 const SkRect& pathBounds = path.getBounds();
2170
Mike Reed38992392019-07-30 10:48:15 -04002171 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002172 while (iter.next()) {
2173 iter.fDevice->drawShadow(path, rec);
2174 }
Mike Reed38992392019-07-30 10:48:15 -04002175 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002176}
2177
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002178void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002179 QuadAAFlags aaFlags, const SkColor4f& color,
2180 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002181 TRACE_EVENT0("skia", TRACE_FUNC);
2182 // Make sure the rect is sorted before passing it along
2183 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2184}
2185
2186void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2187 const SkPoint dstClips[],
2188 const SkMatrix preViewMatrices[],
2189 const SkPaint* paint,
2190 SrcRectConstraint constraint) {
2191 TRACE_EVENT0("skia", TRACE_FUNC);
2192 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2193}
2194
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195//////////////////////////////////////////////////////////////////////////////
2196// These are the virtual drawing methods
2197//////////////////////////////////////////////////////////////////////////////
2198
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002199void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002200 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002201 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2202 }
2203}
2204
reed41af9662015-01-05 07:49:08 -08002205void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002206 this->internalDrawPaint(paint);
2207}
2208
2209void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002210 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211
2212 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002213 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214 }
2215
Mike Reed38992392019-07-30 10:48:15 -04002216 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217}
2218
reed41af9662015-01-05 07:49:08 -08002219void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2220 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221 if ((long)count <= 0) {
2222 return;
2223 }
2224
Mike Reed822128b2017-02-28 16:41:03 -05002225 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002226 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002227 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002228 // special-case 2 points (common for drawing a single line)
2229 if (2 == count) {
2230 r.set(pts[0], pts[1]);
2231 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002232 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002233 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002234 if (!r.isFinite()) {
2235 return;
2236 }
Mike Reed822128b2017-02-28 16:41:03 -05002237 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002238 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2239 return;
2240 }
2241 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002242 }
reed@google.coma584aed2012-05-16 14:06:02 +00002243
halcanary96fcdcc2015-08-27 07:41:13 -07002244 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245
Mike Reed38992392019-07-30 10:48:15 -04002246 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002247
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002249 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 }
reed@google.com4b226022011-01-11 18:32:13 +00002251
Mike Reed38992392019-07-30 10:48:15 -04002252 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253}
2254
reed4a167172016-08-18 17:15:25 -07002255static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002256 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002257}
2258
reed41af9662015-01-05 07:49:08 -08002259void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002260 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002262 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002263 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002264 return;
2265 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266 }
reed@google.com4b226022011-01-11 18:32:13 +00002267
reed4a167172016-08-18 17:15:25 -07002268 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002269 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270
reed4a167172016-08-18 17:15:25 -07002271 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002272 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002273 }
2274
Mike Reed38992392019-07-30 10:48:15 -04002275 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002276 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002277 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002278 SkDrawIter iter(this);
2279 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002280 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002281 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283}
2284
msarett44df6512016-08-25 13:54:30 -07002285void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002286 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002287 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002288 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002289 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2290 return;
2291 }
msarett44df6512016-08-25 13:54:30 -07002292 }
2293
Mike Reed38992392019-07-30 10:48:15 -04002294 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002295
2296 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002297 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002298 }
2299
Mike Reed38992392019-07-30 10:48:15 -04002300 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002301}
2302
Mike Reedd5674082019-04-19 15:00:47 -04002303void SkCanvas::onDrawBehind(const SkPaint& paint) {
2304 SkIRect bounds;
2305 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2306 for (;;) {
2307 const MCRec* rec = (const MCRec*)iter.prev();
2308 if (!rec) {
2309 return; // no backimages, so nothing to draw
2310 }
2311 if (rec->fBackImage) {
2312 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2313 rec->fBackImage->fImage->width(),
2314 rec->fBackImage->fImage->height());
2315 break;
2316 }
2317 }
2318
Mike Reed38992392019-07-30 10:48:15 -04002319 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002320
2321 while (iter.next()) {
2322 SkBaseDevice* dev = iter.fDevice;
2323
Mike Reedd5674082019-04-19 15:00:47 -04002324 dev->save();
2325 // We use clipRegion because it is already defined to operate in dev-space
2326 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2327 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002328 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002329 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002330 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002331 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002332 }
2333
Mike Reed38992392019-07-30 10:48:15 -04002334 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002335}
2336
reed41af9662015-01-05 07:49:08 -08002337void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002338 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002339 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002340 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002341 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002342 return;
2343 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002344 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002345
Mike Reed38992392019-07-30 10:48:15 -04002346 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002347
2348 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002349 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002350 }
2351
Mike Reed38992392019-07-30 10:48:15 -04002352 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002353}
2354
bsalomonac3aa242016-08-19 11:25:19 -07002355void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2356 SkScalar sweepAngle, bool useCenter,
2357 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002358 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002359 if (paint.canComputeFastBounds()) {
2360 SkRect storage;
2361 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002362 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002363 return;
2364 }
bsalomonac3aa242016-08-19 11:25:19 -07002365 }
2366
Mike Reed38992392019-07-30 10:48:15 -04002367 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002368
2369 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002370 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002371 }
2372
Mike Reed38992392019-07-30 10:48:15 -04002373 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002374}
2375
reed41af9662015-01-05 07:49:08 -08002376void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002377 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002378 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002379 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2380 return;
2381 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002382 }
2383
2384 if (rrect.isRect()) {
2385 // call the non-virtual version
2386 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002387 return;
2388 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002389 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002390 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2391 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002392 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002393
Mike Reed38992392019-07-30 10:48:15 -04002394 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002395
2396 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002397 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002398 }
2399
Mike Reed38992392019-07-30 10:48:15 -04002400 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002401}
2402
Mike Reed822128b2017-02-28 16:41:03 -05002403void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002404 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002405 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002406 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2407 return;
2408 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002409 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002410
Mike Reed38992392019-07-30 10:48:15 -04002411 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002412
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002413 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002414 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002415 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002416
Mike Reed38992392019-07-30 10:48:15 -04002417 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002418}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002419
reed41af9662015-01-05 07:49:08 -08002420void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002421 if (!path.isFinite()) {
2422 return;
2423 }
2424
Mike Reed822128b2017-02-28 16:41:03 -05002425 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002426 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002427 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002428 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2429 return;
2430 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002432
Mike Reed822128b2017-02-28 16:41:03 -05002433 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002434 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002435 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002436 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002437 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002438 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439
Mike Reed38992392019-07-30 10:48:15 -04002440 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441
2442 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002443 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 }
2445
Mike Reed38992392019-07-30 10:48:15 -04002446 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002447}
2448
reed262a71b2015-12-05 13:07:27 -08002449bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002450 if (!paint.getImageFilter()) {
2451 return false;
2452 }
2453
2454 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002455 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002456 return false;
2457 }
2458
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002459 // The other paint effects need to be applied before the image filter, but the sprite draw
2460 // applies the filter explicitly first.
2461 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2462 return false;
2463 }
reed262a71b2015-12-05 13:07:27 -08002464 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2465 // Once we can filter and the filter will return a result larger than itself, we should be
2466 // able to remove this constraint.
2467 // skbug.com/4526
2468 //
2469 SkPoint pt;
2470 ctm.mapXY(x, y, &pt);
2471 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2472 return ir.contains(fMCRec->fRasterClip.getBounds());
2473}
2474
Mike Reedf441cfc2018-04-11 14:50:16 -04002475// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2476// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2477// null.
2478static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2479 if (paintParam) {
2480 *real = *paintParam;
2481 real->setStyle(SkPaint::kFill_Style);
2482 real->setPathEffect(nullptr);
2483 paintParam = real;
2484 }
2485 return paintParam;
2486}
2487
reeda85d4d02015-05-06 12:56:48 -07002488void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002489 SkPaint realPaint;
2490 paint = init_image_paint(&realPaint, paint);
2491
reeda85d4d02015-05-06 12:56:48 -07002492 SkRect bounds = SkRect::MakeXYWH(x, y,
2493 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002494 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002495 SkRect tmp = bounds;
2496 if (paint) {
2497 paint->computeFastBounds(tmp, &tmp);
2498 }
2499 if (this->quickReject(tmp)) {
2500 return;
2501 }
reeda85d4d02015-05-06 12:56:48 -07002502 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002503 // At this point we need a real paint object. If the caller passed null, then we should
2504 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2505 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2506 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002507
reeda2217ef2016-07-20 06:04:34 -07002508 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002509 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2510 *paint);
2511 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002512 special = this->getDevice()->makeSpecial(image);
2513 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002514 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002515 }
2516 }
2517
Mike Reed38992392019-07-30 10:48:15 -04002518 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002519
reeda85d4d02015-05-06 12:56:48 -07002520 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002521 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002522 if (special) {
2523 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002524 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002525 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002526 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002527 SkScalarRoundToInt(pt.fY), pnt,
2528 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002529 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002530 iter.fDevice->drawImageRect(
2531 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2532 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002533 }
reeda85d4d02015-05-06 12:56:48 -07002534 }
halcanary9d524f22016-03-29 09:03:52 -07002535
Mike Reed38992392019-07-30 10:48:15 -04002536 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002537}
2538
reed41af9662015-01-05 07:49:08 -08002539void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002540 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002541 SkPaint realPaint;
2542 paint = init_image_paint(&realPaint, paint);
2543
halcanary96fcdcc2015-08-27 07:41:13 -07002544 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002545 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002546 if (paint) {
2547 paint->computeFastBounds(dst, &storage);
2548 }
2549 if (this->quickReject(storage)) {
2550 return;
2551 }
reeda85d4d02015-05-06 12:56:48 -07002552 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002553 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002554
Mike Reed38992392019-07-30 10:48:15 -04002555 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002556
reeda85d4d02015-05-06 12:56:48 -07002557 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002558 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002559 }
halcanary9d524f22016-03-29 09:03:52 -07002560
Mike Reed38992392019-07-30 10:48:15 -04002561 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002562}
2563
reed4c21dc52015-06-25 12:32:03 -07002564void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2565 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002566 SkPaint realPaint;
2567 paint = init_image_paint(&realPaint, paint);
2568
halcanary96fcdcc2015-08-27 07:41:13 -07002569 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002570 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002571 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2572 return;
2573 }
reed@google.com3d608122011-11-21 15:16:16 +00002574 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002575 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002576
Mike Reed38992392019-07-30 10:48:15 -04002577 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002578
reed4c21dc52015-06-25 12:32:03 -07002579 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002580 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002581 }
halcanary9d524f22016-03-29 09:03:52 -07002582
Mike Reed38992392019-07-30 10:48:15 -04002583 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002584}
2585
msarett16882062016-08-16 09:31:08 -07002586void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2587 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002588 SkPaint realPaint;
2589 paint = init_image_paint(&realPaint, paint);
2590
msarett16882062016-08-16 09:31:08 -07002591 if (nullptr == paint || paint->canComputeFastBounds()) {
2592 SkRect storage;
2593 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2594 return;
2595 }
2596 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002597 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002598
Mike Reed38992392019-07-30 10:48:15 -04002599 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002600
2601 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002602 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002603 }
2604
Mike Reed38992392019-07-30 10:48:15 -04002605 DRAW_END
msarett16882062016-08-16 09:31:08 -07002606}
2607
fmalita00d5c2c2014-08-21 08:53:26 -07002608void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2609 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002610 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002611 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002612 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002613 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002614 SkRect tmp;
2615 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2616 return;
2617 }
2618 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002619 }
2620
fmalita024f9962015-03-03 19:08:17 -08002621 // We cannot filter in the looper as we normally do, because the paint is
2622 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002623 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002624
fmalitaaa1b9122014-08-28 14:32:24 -07002625 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002626 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002627 }
2628
Mike Reed38992392019-07-30 10:48:15 -04002629 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002630}
2631
Mike Reed358fcad2018-11-23 15:27:51 -05002632// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002633void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002634 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2635 TRACE_EVENT0("skia", TRACE_FUNC);
2636 if (byteLength) {
2637 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002638 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002639 }
2640}
Mike Reed4f81bb72019-01-23 09:23:00 -05002641
fmalita00d5c2c2014-08-21 08:53:26 -07002642void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2643 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002644 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002645 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002646 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002647 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002648}
reed@google.come0d9ce82014-04-23 04:00:17 +00002649
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002650#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002651void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002652 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002653 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002654
2655 while (iter.next()) {
2656 // In the common case of one iteration we could std::move vertices here.
Mike Reed5caf9352020-03-02 14:57:09 -05002657 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002658 }
2659
Mike Reed38992392019-07-30 10:48:15 -04002660 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002661}
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002662#else
2663void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2664 const SkPaint& paint) {
2665 DRAW_BEGIN(paint, nullptr)
2666
2667 while (iter.next()) {
2668 // In the common case of one iteration we could std::move vertices here.
2669 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2670 }
2671
2672 DRAW_END
2673}
2674#endif
Brian Salomon199fb872017-02-06 09:41:10 -05002675
dandovb3c9d1c2014-08-12 08:34:29 -07002676void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002677 const SkPoint texCoords[4], SkBlendMode bmode,
2678 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002679 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002680 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002681 return;
2682 }
mtklein6cfa73a2014-08-13 13:33:49 -07002683
Mike Reedfaba3712016-11-03 14:45:31 -04002684 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002685}
2686
2687void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002688 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002689 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002690 // Since a patch is always within the convex hull of the control points, we discard it when its
2691 // bounding rectangle is completely outside the current clip.
2692 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002693 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002694 if (this->quickReject(bounds)) {
2695 return;
2696 }
mtklein6cfa73a2014-08-13 13:33:49 -07002697
Mike Reed38992392019-07-30 10:48:15 -04002698 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002699
dandovecfff212014-08-04 10:02:00 -07002700 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002701 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002702 }
mtklein6cfa73a2014-08-13 13:33:49 -07002703
Mike Reed38992392019-07-30 10:48:15 -04002704 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002705}
2706
reeda8db7282015-07-07 10:22:31 -07002707void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002708#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002709 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002710#endif
reede3b38ce2016-01-08 09:18:44 -08002711 RETURN_ON_NULL(dr);
2712 if (x || y) {
2713 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2714 this->onDrawDrawable(dr, &matrix);
2715 } else {
2716 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002717 }
2718}
2719
reeda8db7282015-07-07 10:22:31 -07002720void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002721#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002722 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002723#endif
reede3b38ce2016-01-08 09:18:44 -08002724 RETURN_ON_NULL(dr);
2725 if (matrix && matrix->isIdentity()) {
2726 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002727 }
reede3b38ce2016-01-08 09:18:44 -08002728 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002729}
2730
2731void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002732 // drawable bounds are no longer reliable (e.g. android displaylist)
2733 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002734 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002735}
2736
reed71c3c762015-06-24 10:29:17 -07002737void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002738 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002739 const SkRect* cull, const SkPaint* paint) {
2740 if (cull && this->quickReject(*cull)) {
2741 return;
2742 }
2743
2744 SkPaint pnt;
2745 if (paint) {
2746 pnt = *paint;
2747 }
halcanary9d524f22016-03-29 09:03:52 -07002748
Mike Reed38992392019-07-30 10:48:15 -04002749 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002750 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002751 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002752 }
Mike Reed38992392019-07-30 10:48:15 -04002753 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002754}
2755
reedf70b5312016-03-04 16:36:20 -08002756void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2757 SkASSERT(key);
2758
2759 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002760 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002761 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002762 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002763 }
Mike Reed38992392019-07-30 10:48:15 -04002764 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002765}
2766
Michael Ludwiga595f862019-08-27 15:25:49 -04002767void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2768 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002769 SkASSERT(r.isSorted());
2770
2771 // If this used a paint, it would be a filled color with blend mode, which does not
2772 // need to use an autodraw loop, so use SkDrawIter directly.
2773 if (this->quickReject(r)) {
2774 return;
2775 }
2776
Michael Ludwiga4b44882019-08-28 14:34:58 -04002777 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002778 SkDrawIter iter(this);
2779 while(iter.next()) {
2780 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2781 }
2782}
2783
2784void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2785 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2786 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002787 if (count <= 0) {
2788 // Nothing to draw
2789 return;
2790 }
2791
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002792 SkPaint realPaint;
2793 init_image_paint(&realPaint, paint);
2794
Michael Ludwiga4b44882019-08-28 14:34:58 -04002795 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2796 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2797 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2798 // or we need it for the autolooper (since it greatly improves image filter perf).
2799 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2800 bool setBoundsValid = count == 1 || needsAutoLooper;
2801 SkRect setBounds = imageSet[0].fDstRect;
2802 if (imageSet[0].fMatrixIndex >= 0) {
2803 // Account for the per-entry transform that is applied prior to the CTM when drawing
2804 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002805 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002806 if (needsAutoLooper) {
2807 for (int i = 1; i < count; ++i) {
2808 SkRect entryBounds = imageSet[i].fDstRect;
2809 if (imageSet[i].fMatrixIndex >= 0) {
2810 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2811 }
2812 setBounds.joinPossiblyEmptyRect(entryBounds);
2813 }
2814 }
2815
2816 // If we happen to have the draw bounds, though, might as well check quickReject().
2817 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2818 SkRect tmp;
2819 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2820 return;
2821 }
2822 }
2823
2824 if (needsAutoLooper) {
2825 SkASSERT(setBoundsValid);
2826 DRAW_BEGIN(realPaint, &setBounds)
2827 while (iter.next()) {
2828 iter.fDevice->drawEdgeAAImageSet(
2829 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2830 }
2831 DRAW_END
2832 } else {
2833 this->predrawNotify();
2834 SkDrawIter iter(this);
2835 while(iter.next()) {
2836 iter.fDevice->drawEdgeAAImageSet(
2837 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2838 }
2839 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002840}
2841
reed@android.com8a1c16f2008-12-17 15:59:43 +00002842//////////////////////////////////////////////////////////////////////////////
2843// These methods are NOT virtual, and therefore must call back into virtual
2844// methods, rather than actually drawing themselves.
2845//////////////////////////////////////////////////////////////////////////////
2846
reed374772b2016-10-05 17:33:02 -07002847void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002849 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002850 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002851 this->drawPaint(paint);
2852}
2853
2854void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002855 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002856 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2857}
2858
Mike Reed3661bc92017-02-22 13:21:42 -05002859void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002860 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002861 pts[0].set(x0, y0);
2862 pts[1].set(x1, y1);
2863 this->drawPoints(kLines_PointMode, 2, pts, paint);
2864}
2865
Mike Reed3661bc92017-02-22 13:21:42 -05002866void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867 if (radius < 0) {
2868 radius = 0;
2869 }
2870
2871 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002872 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002873 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874}
2875
2876void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2877 const SkPaint& paint) {
2878 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002879 SkRRect rrect;
2880 rrect.setRectXY(r, rx, ry);
2881 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002882 } else {
2883 this->drawRect(r, paint);
2884 }
2885}
2886
reed@android.com8a1c16f2008-12-17 15:59:43 +00002887void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2888 SkScalar sweepAngle, bool useCenter,
2889 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002890 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002891 if (oval.isEmpty() || !sweepAngle) {
2892 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002893 }
bsalomon21af9ca2016-08-25 12:29:23 -07002894 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002895}
2896
reed@android.comf76bacf2009-05-13 14:00:33 +00002897///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002898#ifdef SK_DISABLE_SKPICTURE
2899void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002900
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002901
2902void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2903 const SkPaint* paint) {}
2904#else
Mike Klein88d90712018-01-27 17:30:04 +00002905/**
2906 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2907 * against the playback cost of recursing into the subpicture to get at its actual ops.
2908 *
2909 * For now we pick a conservatively small value, though measurement (and other heuristics like
2910 * the type of ops contained) may justify changing this value.
2911 */
2912#define kMaxPictureOpsToUnrollInsteadOfRef 1
2913
reedd5fa1a42014-08-09 11:08:05 -07002914void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002915 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002916 RETURN_ON_NULL(picture);
2917
reede3b38ce2016-01-08 09:18:44 -08002918 if (matrix && matrix->isIdentity()) {
2919 matrix = nullptr;
2920 }
Mike Klein88d90712018-01-27 17:30:04 +00002921 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2922 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2923 picture->playback(this);
2924 } else {
2925 this->onDrawPicture(picture, matrix, paint);
2926 }
reedd5fa1a42014-08-09 11:08:05 -07002927}
robertphillips9b14f262014-06-04 05:40:44 -07002928
reedd5fa1a42014-08-09 11:08:05 -07002929void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2930 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002931 if (!paint || paint->canComputeFastBounds()) {
2932 SkRect bounds = picture->cullRect();
2933 if (paint) {
2934 paint->computeFastBounds(bounds, &bounds);
2935 }
2936 if (matrix) {
2937 matrix->mapRect(&bounds);
2938 }
2939 if (this->quickReject(bounds)) {
2940 return;
2941 }
2942 }
2943
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002944 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002945 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002946}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002947#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002948
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949///////////////////////////////////////////////////////////////////////////////
2950///////////////////////////////////////////////////////////////////////////////
2951
reed3aafe112016-08-18 12:45:34 -07002952SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002953 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002954
2955 SkASSERT(canvas);
2956
reed3aafe112016-08-18 12:45:34 -07002957 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958 fDone = !fImpl->next();
2959}
2960
2961SkCanvas::LayerIter::~LayerIter() {
2962 fImpl->~SkDrawIter();
2963}
2964
2965void SkCanvas::LayerIter::next() {
2966 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002967 if (!fDone) {
2968 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
2969 // filters, so its devices will always be able to report the origin exactly.
2970 fDeviceOrigin = fImpl->fDevice->getOrigin();
2971 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002972}
2973
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002974SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002975 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002976}
2977
2978const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002979 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002980}
2981
2982const SkPaint& SkCanvas::LayerIter::paint() const {
2983 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002984 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002985 paint = &fDefaultPaint;
2986 }
2987 return *paint;
2988}
2989
Mike Reedca37f322018-03-08 13:22:16 -05002990SkIRect SkCanvas::LayerIter::clipBounds() const {
2991 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05002992}
2993
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002994int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
2995int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002996
2997///////////////////////////////////////////////////////////////////////////////
2998
Brian Osmane8a98632019-04-10 10:26:10 -04002999SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3000SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3001SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3002SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3003
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003004SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3005 const SkRect& dstRect, int matrixIndex, float alpha,
3006 unsigned aaFlags, bool hasClip)
3007 : fImage(std::move(image))
3008 , fSrcRect(srcRect)
3009 , fDstRect(dstRect)
3010 , fMatrixIndex(matrixIndex)
3011 , fAlpha(alpha)
3012 , fAAFlags(aaFlags)
3013 , fHasClip(hasClip) {}
3014
3015SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3016 const SkRect& dstRect, float alpha, unsigned aaFlags)
3017 : fImage(std::move(image))
3018 , fSrcRect(srcRect)
3019 , fDstRect(dstRect)
3020 , fAlpha(alpha)
3021 , fAAFlags(aaFlags) {}
3022
3023///////////////////////////////////////////////////////////////////////////////
3024
Mike Reed5df49342016-11-12 08:06:55 -06003025std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003026 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003027 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003028 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003029 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003030
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003031 SkBitmap bitmap;
3032 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003033 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003034 }
Mike Reed12f77342017-11-08 11:19:52 -05003035
3036 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003037 std::make_unique<SkCanvas>(bitmap, *props) :
3038 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003039}
reedd5fa1a42014-08-09 11:08:05 -07003040
3041///////////////////////////////////////////////////////////////////////////////
3042
Florin Malitaee424ac2016-12-01 12:47:59 -05003043SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003044 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003045
Florin Malita439ace92016-12-02 12:05:41 -05003046SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003047 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003048
Herb Derbyefe39bc2018-05-01 17:06:20 -04003049SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003050 : INHERITED(device) {}
3051
Florin Malitaee424ac2016-12-01 12:47:59 -05003052SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3053 (void)this->INHERITED::getSaveLayerStrategy(rec);
3054 return kNoLayer_SaveLayerStrategy;
3055}
3056
Mike Reed148b7fd2018-12-18 17:38:18 -05003057bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3058 return false;
3059}
3060
Florin Malitaee424ac2016-12-01 12:47:59 -05003061///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003062
reed73603f32016-09-20 08:42:38 -07003063static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3064static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3065static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3066static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3067static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3068static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003069
3070///////////////////////////////////////////////////////////////////////////////////////////////////
3071
3072SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3073 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003074 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003075 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003076 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003077 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003078 clip.setEmpty();
3079 }
3080
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003081 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003082 return handle;
3083 }
3084 return nullptr;
3085}
3086
3087static bool install(SkBitmap* bm, const SkImageInfo& info,
3088 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003089 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003090}
3091
3092SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3093 SkBitmap* bm) {
3094 SkRasterHandleAllocator::Rec rec;
3095 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3096 return nullptr;
3097 }
3098 return rec.fHandle;
3099}
3100
3101std::unique_ptr<SkCanvas>
3102SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3103 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003104 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003105 return nullptr;
3106 }
3107
3108 SkBitmap bm;
3109 Handle hndl;
3110
3111 if (rec) {
3112 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3113 } else {
3114 hndl = alloc->allocBitmap(info, &bm);
3115 }
3116 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3117}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003118
3119///////////////////////////////////////////////////////////////////////////////////////////////////