blob: e9b0af9a07ded5b2bb3dd6dc5fb06a02cd5703d5 [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;
1086 const SkPaint* paint = rec.fPaint;
1087 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1088
Mike Reed5532c2a2019-02-23 12:00:32 -05001089 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1090 // regardless of any hint-rect from the caller. skbug.com/8783
1091 if (rec.fBackdrop) {
1092 bounds = nullptr;
1093 }
1094
reed8c30a812016-04-20 16:36:51 -07001095 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001096 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001097 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001098 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001099
reed8c30a812016-04-20 16:36:51 -07001100 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001101 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1102 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1103 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001104 *
1105 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001106 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1107 * if necessary.
1108 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1109 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001110 * 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 -04001111 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001112 * of the original imagefilter, and draw that (via drawSprite)
1113 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1114 *
1115 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1116 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1117 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001118 if (imageFilter) {
1119 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001120 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1121 &modifiedCTM);
1122 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001123 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001124 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001125 modifiedRec = fMCRec;
1126 this->internalSetMatrix(modifiedCTM);
1127 SkPaint* p = lazyP.set(*paint);
1128 p->setImageFilter(std::move(modifiedFilter));
1129 imageFilter = p->getImageFilter();
1130 paint = p;
1131 }
1132 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1133 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001134 }
reed8c30a812016-04-20 16:36:51 -07001135
junov@chromium.orga907ac32012-02-24 21:54:07 +00001136 // do this before we create the layer. We don't call the public save() since
1137 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001138 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001139
junov@chromium.orga907ac32012-02-24 21:54:07 +00001140 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001141 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001142 if (modifiedRec) {
1143 // In this case there will be no layer in which to stash the matrix so we need to
1144 // revert the prior MCRec to its earlier state.
1145 modifiedRec->fMatrix = stashedMatrix;
1146 }
reed2ff1fce2014-12-11 07:07:37 -08001147 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 }
1149
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001150 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1151 // the clipRectBounds() call above?
1152 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001153 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001154 }
1155
reed8dc0ccb2015-03-20 06:32:52 -07001156 SkPixelGeometry geo = fProps.pixelGeometry();
1157 if (paint) {
reed76033be2015-03-14 10:54:31 -07001158 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001159 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001160 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001161 }
1162 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163
robertphillips5139e502016-07-19 05:10:40 -07001164 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001165 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001166 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001167 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001168 }
reedb2db8982014-11-13 12:41:02 -08001169
Mike Kleine083f7c2018-02-07 12:54:27 -05001170 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001171 if (rec.fSaveLayerFlags & kF16ColorType) {
1172 info = info.makeColorType(kRGBA_F16_SkColorType);
1173 }
reed129ed1c2016-02-22 06:42:31 -08001174
Hal Canary704cd322016-11-07 14:13:52 -05001175 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001176 {
Florin Malita4571e492019-07-16 10:25:58 -04001177 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001178 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001179 const bool trackCoverage =
1180 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001181 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001182 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001183 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001184 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1185 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001186 return;
reed61f501f2015-04-29 08:34:00 -07001187 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001188 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001189 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190
Mike Reedb43a3e02017-02-11 10:18:58 -05001191 // only have a "next" if this new layer doesn't affect the clip (rare)
1192 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 fMCRec->fLayer = layer;
1194 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001195
Mike Reedc61abee2017-02-28 17:45:27 -05001196 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001197 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001198 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001199 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001200
Mike Reedc42a1cd2017-02-14 14:25:14 -05001201 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1202
1203 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1204 if (layer->fNext) {
1205 // need to punch a hole in the previous device, so we don't draw there, given that
1206 // the new top-layer will allow drawing to happen "below" it.
1207 SkRegion hole(ir);
1208 do {
1209 layer = layer->fNext;
1210 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1211 } while (layer->fNext);
1212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213}
1214
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001215int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001216 if (0xFF == alpha) {
1217 return this->saveLayer(bounds, nullptr);
1218 } else {
1219 SkPaint tmpPaint;
1220 tmpPaint.setAlpha(alpha);
1221 return this->saveLayer(bounds, &tmpPaint);
1222 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001223}
1224
Mike Reed148b7fd2018-12-18 17:38:18 -05001225void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001226 SkBaseDevice* device = this->getTopDevice();
1227 if (nullptr == device) { // Do we still need this check???
1228 return;
1229 }
1230
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001231 // Map the local bounds into the top device's coordinate space (this is not
1232 // necessarily the full global CTM transform).
1233 SkIRect devBounds;
1234 if (localBounds) {
1235 SkRect tmp;
1236 device->localToDevice().mapRect(&tmp, *localBounds);
1237 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1238 devBounds.setEmpty();
1239 }
1240 } else {
1241 devBounds = device->devClipBounds();
1242 }
1243 if (devBounds.isEmpty()) {
1244 return;
1245 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001246
Michael Ludwigac352122019-08-28 21:03:05 +00001247 // This is getting the special image from the current device, which is then drawn into (both by
1248 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1249 // own device, we need to explicitly copy the back image contents so that its original content
1250 // is available when we splat it back later during restore.
1251 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001252 if (!backImage) {
1253 return;
1254 }
1255
1256 // we really need the save, so we can wack the fMCRec
1257 this->checkForDeferredSave();
1258
1259 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1260
1261 SkPaint paint;
1262 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001263 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001264}
1265
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266void SkCanvas::internalRestore() {
1267 SkASSERT(fMCStack.count() != 0);
1268
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001269 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 DeviceCM* layer = fMCRec->fLayer; // may be null
1271 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001272 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273
Mike Reed148b7fd2018-12-18 17:38:18 -05001274 // move this out before we do the actual restore
1275 auto backImage = std::move(fMCRec->fBackImage);
1276
Mike Reedb18e74d2020-01-16 13:58:22 -05001277 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1278 fCameraStack.pop_back();
1279 }
1280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 // now do the normal restore()
1282 fMCRec->~MCRec(); // balanced in save()
1283 fMCStack.pop_back();
1284 fMCRec = (MCRec*)fMCStack.back();
1285
Mike Reedc42a1cd2017-02-14 14:25:14 -05001286 if (fMCRec) {
1287 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1288 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001289
Mike Reed148b7fd2018-12-18 17:38:18 -05001290 if (backImage) {
1291 SkPaint paint;
1292 paint.setBlendMode(SkBlendMode::kDstOver);
1293 const int x = backImage->fLoc.x();
1294 const int y = backImage->fLoc.y();
1295 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1296 nullptr, SkMatrix::I());
1297 }
1298
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1300 since if we're being recorded, we don't want to record this (the
1301 recorder will have already recorded the restore).
1302 */
bsalomon49f085d2014-09-05 13:34:00 -07001303 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001304 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001305 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001306 // At this point, 'layer' has been removed from the device stack, so the devices that
1307 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1308 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001309 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001310 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001311 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001312 delete layer;
reedb679ca82015-04-07 04:40:48 -07001313 } else {
1314 // we're at the root
reeda499f902015-05-01 09:34:31 -07001315 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001316 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001317 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001319 }
msarettfbfa2582016-08-12 08:29:08 -07001320
1321 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001322 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001323 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325}
1326
reede8f30622016-03-23 18:59:25 -07001327sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001328 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001329 props = &fProps;
1330 }
1331 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001332}
1333
reede8f30622016-03-23 18:59:25 -07001334sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001335 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001336 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001337}
1338
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001339SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001340 return this->onImageInfo();
1341}
1342
1343SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001344 SkBaseDevice* dev = this->getDevice();
1345 if (dev) {
1346 return dev->imageInfo();
1347 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001348 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001349 }
1350}
1351
brianosman898235c2016-04-06 07:38:23 -07001352bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001353 return this->onGetProps(props);
1354}
1355
1356bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001357 SkBaseDevice* dev = this->getDevice();
1358 if (dev) {
1359 if (props) {
1360 *props = fProps;
1361 }
1362 return true;
1363 } else {
1364 return false;
1365 }
1366}
1367
reed6ceeebd2016-03-09 14:26:26 -08001368bool SkCanvas::peekPixels(SkPixmap* pmap) {
1369 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001370}
1371
reed884e97c2015-05-26 11:31:54 -07001372bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001374 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375}
1376
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001377void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001378 SkPixmap pmap;
1379 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001380 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001381 }
1382 if (info) {
1383 *info = pmap.info();
1384 }
1385 if (rowBytes) {
1386 *rowBytes = pmap.rowBytes();
1387 }
1388 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001389 // If the caller requested the origin, they presumably are expecting the returned pixels to
1390 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1391 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1392 // instead of an origin, just don't expose the pixels in that case. Note that this means
1393 // that layers with complex coordinate spaces can still report their pixels if the caller
1394 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1395 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1396 *origin = this->getTopDevice()->getOrigin();
1397 } else {
1398 return nullptr;
1399 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001400 }
reed884e97c2015-05-26 11:31:54 -07001401 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001402}
1403
reed884e97c2015-05-26 11:31:54 -07001404bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001405 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001406 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001407}
1408
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
Mike Reed8bcd1282019-03-13 16:51:54 -04001411// In our current design/features, we should never have a layer (src) in a different colorspace
1412// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1413// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1414// colorspace.
1415static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1416 SkASSERT(src == dst);
1417}
1418
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001419void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001420 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001422 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 paint = &tmp;
1424 }
reed@google.com4b226022011-01-11 18:32:13 +00001425
Mike Reed38992392019-07-30 10:48:15 -04001426 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001429 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001430 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1431 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001432 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001433 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001434 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1435 // so it should always be possible to use the relative origin. Once drawDevice() and
1436 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1437 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001438 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001439 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1440 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001441 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1442 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001443 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1444 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001445 }
reed@google.com76dd2772012-01-05 21:15:07 +00001446 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001447 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001448 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 }
reeda2217ef2016-07-20 06:04:34 -07001450
Mike Reed38992392019-07-30 10:48:15 -04001451 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452}
1453
reed32704672015-12-16 08:27:10 -08001454/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001455
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001456void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001457 if (dx || dy) {
1458 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001459 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001460
reedfe69b502016-09-12 06:31:48 -07001461 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001462 // However, if either is non-finite, we might still complicate the matrix type,
1463 // so we still have to compute this.
1464 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001465
Mike Reedc42a1cd2017-02-14 14:25:14 -05001466 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001467
reedfe69b502016-09-12 06:31:48 -07001468 this->didTranslate(dx,dy);
1469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001472void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001473 if (sx != 1 || sy != 1) {
1474 this->checkForDeferredSave();
1475 fMCRec->fMatrix.preScale(sx, sy);
1476
1477 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1478 // but pre-scaling by a non-finite does change it, so we have to recompute.
1479 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1480
1481 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1482
1483 this->didScale(sx, sy);
1484 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485}
1486
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001487void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001488 SkMatrix m;
1489 m.setRotate(degrees);
1490 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491}
1492
bungeman7438bfc2016-07-12 15:01:19 -07001493void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1494 SkMatrix m;
1495 m.setRotate(degrees, px, py);
1496 this->concat(m);
1497}
1498
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001499void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001500 SkMatrix m;
1501 m.setSkew(sx, sy);
1502 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001503}
1504
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001505void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001506 if (matrix.isIdentity()) {
1507 return;
1508 }
1509
reed2ff1fce2014-12-11 07:07:37 -08001510 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001511 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001512
msarett9637ea92016-08-18 14:03:30 -07001513 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001514
Mike Reed7627fa52017-02-08 10:07:53 -05001515 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001516
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001517 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001518}
1519
Mike Reed46f5c5f2020-02-20 15:42:29 -05001520void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001521 this->checkForDeferredSave();
1522
Mike Reed46f5c5f2020-02-20 15:42:29 -05001523 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001524
1525 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1526
1527 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1528
Mike Reed46f5c5f2020-02-20 15:42:29 -05001529 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001530}
Mike Reed064c7f92020-01-08 17:33:04 -05001531
Mike Reed46f5c5f2020-02-20 15:42:29 -05001532void SkCanvas::concat44(const SkM44& m) {
1533 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001534}
1535
reed8c30a812016-04-20 16:36:51 -07001536void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001537 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001538 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001539
Mike Reedc42a1cd2017-02-14 14:25:14 -05001540 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001541}
1542
1543void SkCanvas::setMatrix(const SkMatrix& matrix) {
1544 this->checkForDeferredSave();
1545 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001546 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547}
1548
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001550 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551}
1552
1553//////////////////////////////////////////////////////////////////////////////
1554
Mike Reedc1f77742016-12-09 09:00:50 -05001555void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001556 if (!rect.isFinite()) {
1557 return;
1558 }
reed2ff1fce2014-12-11 07:07:37 -08001559 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001560 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1561 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562}
1563
Mike Reedc1f77742016-12-09 09:00:50 -05001564void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001565 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001566
Mike Reed7627fa52017-02-08 10:07:53 -05001567 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001568
reedc64eff52015-11-21 12:39:45 -08001569 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001570 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1571 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001572 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573}
1574
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001575void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1576 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001577 if (fClipRestrictionRect.isEmpty()) {
1578 // we notify the device, but we *dont* resolve deferred saves (since we're just
1579 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001580 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001581 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001582 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001583 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001584 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001585 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001586 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1587 }
1588}
1589
Mike Reedc1f77742016-12-09 09:00:50 -05001590void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001591 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001592 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001593 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1595 } else {
1596 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001597 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001599
Mike Reedc1f77742016-12-09 09:00:50 -05001600void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001601 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001602
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001604
Mike Reed7627fa52017-02-08 10:07:53 -05001605 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001606
Mike Reed20800c82017-11-15 16:09:04 -05001607 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1608 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001609 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001610}
1611
Mike Reedc1f77742016-12-09 09:00:50 -05001612void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001613 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001615
1616 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1617 SkRect r;
1618 if (path.isRect(&r)) {
1619 this->onClipRect(r, op, edgeStyle);
1620 return;
1621 }
1622 SkRRect rrect;
1623 if (path.isOval(&r)) {
1624 rrect.setOval(r);
1625 this->onClipRRect(rrect, op, edgeStyle);
1626 return;
1627 }
1628 if (path.isRRect(&rrect)) {
1629 this->onClipRRect(rrect, op, edgeStyle);
1630 return;
1631 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001632 }
robertphillips39f05382015-11-24 09:30:12 -08001633
1634 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635}
1636
Mike Reedc1f77742016-12-09 09:00:50 -05001637void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001638 AutoValidateClip avc(this);
1639
Brian Salomona3b45d42016-10-03 11:36:16 -04001640 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001641
Mike Reed7627fa52017-02-08 10:07:53 -05001642 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643
Brian Salomona3b45d42016-10-03 11:36:16 -04001644 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001645 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001646 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001647 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648}
1649
Mike Reedc1f77742016-12-09 09:00:50 -05001650void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001651 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001652 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653}
1654
Mike Reedc1f77742016-12-09 09:00:50 -05001655void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001656 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001657
reed@google.com5c3d1472011-02-22 19:12:23 +00001658 AutoValidateClip avc(this);
1659
Mike Reed20800c82017-11-15 16:09:04 -05001660 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001661 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662}
1663
reed@google.com819c9212011-02-23 18:56:55 +00001664#ifdef SK_DEBUG
1665void SkCanvas::validateClip() const {
1666 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001667 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001668 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001669 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001670 return;
1671 }
reed@google.com819c9212011-02-23 18:56:55 +00001672}
1673#endif
1674
Mike Reeda1361362017-03-07 09:37:29 -05001675bool SkCanvas::androidFramework_isClipAA() const {
1676 bool containsAA = false;
1677
1678 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1679
1680 return containsAA;
1681}
1682
1683class RgnAccumulator {
1684 SkRegion* fRgn;
1685public:
1686 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1687 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1688 SkIPoint origin = device->getOrigin();
1689 if (origin.x() | origin.y()) {
1690 rgn->translate(origin.x(), origin.y());
1691 }
1692 fRgn->op(*rgn, SkRegion::kUnion_Op);
1693 }
1694};
1695
1696void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1697 RgnAccumulator accum(rgn);
1698 SkRegion tmp;
1699
1700 rgn->setEmpty();
1701 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001702}
1703
reed@google.com5c3d1472011-02-22 19:12:23 +00001704///////////////////////////////////////////////////////////////////////////////
1705
reed@google.com754de5f2014-02-24 19:38:20 +00001706bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001707 return fMCRec->fRasterClip.isEmpty();
1708
1709 // TODO: should we only use the conservative answer in a recording canvas?
1710#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001711 SkBaseDevice* dev = this->getTopDevice();
1712 // if no device we return true
1713 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001714#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001715}
1716
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001717bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001718 SkBaseDevice* dev = this->getTopDevice();
1719 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001720 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001721}
1722
msarettfbfa2582016-08-12 08:29:08 -07001723static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1724#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1725 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1726 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1727 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1728 return 0xF != _mm_movemask_ps(mask);
1729#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1730 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1731 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1732 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1733 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1734#else
1735 SkRect devRectAsRect;
1736 SkRect devClipAsRect;
1737 devRect.store(&devRectAsRect.fLeft);
1738 devClip.store(&devClipAsRect.fLeft);
1739 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1740#endif
1741}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001742
msarettfbfa2582016-08-12 08:29:08 -07001743// It's important for this function to not be inlined. Otherwise the compiler will share code
1744// between the fast path and the slow path, resulting in two slow paths.
1745static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1746 const SkMatrix& matrix) {
1747 SkRect deviceRect;
1748 matrix.mapRect(&deviceRect, src);
1749 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1750}
1751
1752bool SkCanvas::quickReject(const SkRect& src) const {
1753#ifdef SK_DEBUG
1754 // Verify that fDeviceClipBounds are set properly.
1755 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001756 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001757 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001758 } else {
msarettfbfa2582016-08-12 08:29:08 -07001759 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760 }
msarettfbfa2582016-08-12 08:29:08 -07001761
msarett9637ea92016-08-18 14:03:30 -07001762 // Verify that fIsScaleTranslate is set properly.
1763 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001764#endif
1765
msarett9637ea92016-08-18 14:03:30 -07001766 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001767 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1768 }
1769
1770 // We inline the implementation of mapScaleTranslate() for the fast path.
1771 float sx = fMCRec->fMatrix.getScaleX();
1772 float sy = fMCRec->fMatrix.getScaleY();
1773 float tx = fMCRec->fMatrix.getTranslateX();
1774 float ty = fMCRec->fMatrix.getTranslateY();
1775 Sk4f scale(sx, sy, sx, sy);
1776 Sk4f trans(tx, ty, tx, ty);
1777
1778 // Apply matrix.
1779 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1780
1781 // Make sure left < right, top < bottom.
1782 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1783 Sk4f min = Sk4f::Min(ltrb, rblt);
1784 Sk4f max = Sk4f::Max(ltrb, rblt);
1785 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1786 // ARM this sequence generates the fastest (a single instruction).
1787 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1788
1789 // Check if the device rect is NaN or outside the clip.
1790 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791}
1792
reed@google.com3b3e8952012-08-16 20:53:31 +00001793bool SkCanvas::quickReject(const SkPath& path) const {
1794 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795}
1796
Mike Klein83c8dd92017-11-28 17:08:45 -05001797SkRect SkCanvas::getLocalClipBounds() const {
1798 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001799 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001800 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801 }
1802
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001803 SkMatrix inverse;
1804 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001805 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001806 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001807 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808
Mike Reed42e8c532017-01-23 14:09:13 -05001809 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001810 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001811 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001812
Mike Reedb57b9312018-04-23 12:12:54 -04001813 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001814 inverse.mapRect(&bounds, r);
1815 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816}
1817
Mike Klein83c8dd92017-11-28 17:08:45 -05001818SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001819 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001820}
1821
Mike Reedb18e74d2020-01-16 13:58:22 -05001822///////////////////////////////////////////////////////////////////////
1823
1824SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1825 : fMCRec(owner)
1826 , fCamera(camera)
1827{
1828 // assumes the mcrec has already been concatenated with the camera
1829 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1830 fInvPostCamera.setIdentity();
1831 }
1832}
1833
Mike Reed403c8072020-01-08 10:40:39 -05001834SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001835 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836}
1837
Mike Reed46f5c5f2020-02-20 15:42:29 -05001838SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001839 return fMCRec->fMatrix;
1840}
1841
Mike Reedc43f2a02020-01-16 14:54:34 -05001842SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001843 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001844 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001845 } else {
1846 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001847 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001848 }
1849}
1850
Mike Reedc43f2a02020-01-16 14:54:34 -05001851SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001852 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001853 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001854 } else {
1855 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001856 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001857 }
1858}
1859
Mike Reed46f5c5f2020-02-20 15:42:29 -05001860void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1861 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001862}
1863
1864void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1865 this->experimental_getLocalToWorld().getColMajor(colMajor);
1866}
1867
1868void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1869 this->experimental_getLocalToCamera().getColMajor(colMajor);
1870}
1871
Brian Osman11052242016-10-27 14:47:55 -04001872GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001873 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001874 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001875}
1876
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001877GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001878 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001879 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001880}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001881
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001882void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1883 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001884 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001885 if (outer.isEmpty()) {
1886 return;
1887 }
1888 if (inner.isEmpty()) {
1889 this->drawRRect(outer, paint);
1890 return;
1891 }
1892
1893 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001894 // be able to return ...
1895 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001896 //
1897 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001898 if (!outer.getBounds().contains(inner.getBounds())) {
1899 return;
1900 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001901
1902 this->onDrawDRRect(outer, inner, paint);
1903}
1904
reed41af9662015-01-05 07:49:08 -08001905void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001906 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001907 this->onDrawPaint(paint);
1908}
1909
1910void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001911 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001912 // To avoid redundant logic in our culling code and various backends, we always sort rects
1913 // before passing them along.
1914 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001915}
1916
Mike Reedd5674082019-04-19 15:00:47 -04001917void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1918 TRACE_EVENT0("skia", TRACE_FUNC);
1919 this->onDrawBehind(paint);
1920}
1921
msarettdca352e2016-08-26 06:37:45 -07001922void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001923 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001924 if (region.isEmpty()) {
1925 return;
1926 }
1927
1928 if (region.isRect()) {
1929 return this->drawIRect(region.getBounds(), paint);
1930 }
1931
1932 this->onDrawRegion(region, paint);
1933}
1934
reed41af9662015-01-05 07:49:08 -08001935void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001936 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001937 // To avoid redundant logic in our culling code and various backends, we always sort rects
1938 // before passing them along.
1939 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001940}
1941
1942void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001943 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001944 this->onDrawRRect(rrect, paint);
1945}
1946
1947void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001948 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001949 this->onDrawPoints(mode, count, pts, paint);
1950}
1951
Mike Reede88a1cb2017-03-17 09:50:46 -04001952void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1953 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001954 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001955}
1956
1957void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001958 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001959 RETURN_ON_NULL(vertices);
Mike Reed5caf9352020-03-02 14:57:09 -05001960 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1961 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
1962 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001963}
1964
1965void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001966 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001967 this->onDrawPath(path, paint);
1968}
1969
reeda85d4d02015-05-06 12:56:48 -07001970void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001971 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001972 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001973 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001974}
1975
Mike Reedc4e31092018-01-30 11:15:27 -05001976// Returns true if the rect can be "filled" : non-empty and finite
1977static bool fillable(const SkRect& r) {
1978 SkScalar w = r.width();
1979 SkScalar h = r.height();
1980 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1981}
1982
reede47829b2015-08-06 10:02:53 -07001983void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1984 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001985 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001986 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001987 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001988 return;
1989 }
1990 this->onDrawImageRect(image, &src, dst, paint, constraint);
1991}
reed41af9662015-01-05 07:49:08 -08001992
reed84984ef2015-07-17 07:09:43 -07001993void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1994 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001995 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001996 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001997}
1998
Brian Salomonf08002c2018-10-26 16:15:46 -04001999void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002000 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002001 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002002 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002003}
reede47829b2015-08-06 10:02:53 -07002004
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002005namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002006class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002007public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002008 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2009 if (!origPaint) {
2010 return;
2011 }
2012 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2013 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2014 }
2015 if (origPaint->getMaskFilter()) {
2016 fPaint.writable()->setMaskFilter(nullptr);
2017 }
2018 if (origPaint->isAntiAlias()) {
2019 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002020 }
2021 }
2022
2023 const SkPaint* get() const {
2024 return fPaint;
2025 }
2026
2027private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002028 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002029};
2030} // namespace
2031
reed4c21dc52015-06-25 12:32:03 -07002032void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2033 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002034 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002035 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002036 if (dst.isEmpty()) {
2037 return;
2038 }
msarett552bca92016-08-03 06:53:26 -07002039 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002040 LatticePaint latticePaint(paint);
2041 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002042 } else {
reede47829b2015-08-06 10:02:53 -07002043 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002044 }
reed4c21dc52015-06-25 12:32:03 -07002045}
2046
msarett16882062016-08-16 09:31:08 -07002047void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2048 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002049 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002050 RETURN_ON_NULL(image);
2051 if (dst.isEmpty()) {
2052 return;
2053 }
msarett71df2d72016-09-30 12:41:42 -07002054
2055 SkIRect bounds;
2056 Lattice latticePlusBounds = lattice;
2057 if (!latticePlusBounds.fBounds) {
2058 bounds = SkIRect::MakeWH(image->width(), image->height());
2059 latticePlusBounds.fBounds = &bounds;
2060 }
2061
2062 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002063 LatticePaint latticePaint(paint);
2064 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002065 } else {
2066 this->drawImageRect(image, dst, paint);
2067 }
2068}
2069
reed41af9662015-01-05 07:49:08 -08002070void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002071 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002072 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002073 return;
2074 }
reed41af9662015-01-05 07:49:08 -08002075 this->onDrawBitmap(bitmap, dx, dy, paint);
2076}
2077
reede47829b2015-08-06 10:02:53 -07002078void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002079 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002080 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002081 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002082 return;
2083 }
reede47829b2015-08-06 10:02:53 -07002084 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002085}
2086
reed84984ef2015-07-17 07:09:43 -07002087void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2088 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002089 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002090}
2091
reede47829b2015-08-06 10:02:53 -07002092void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2093 SrcRectConstraint constraint) {
2094 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2095 constraint);
2096}
reede47829b2015-08-06 10:02:53 -07002097
reed41af9662015-01-05 07:49:08 -08002098void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2099 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002100 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002101 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002102 return;
2103 }
msarett552bca92016-08-03 06:53:26 -07002104 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002105 LatticePaint latticePaint(paint);
2106 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002107 } else {
reeda5517e22015-07-14 10:54:12 -07002108 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002109 }
reed41af9662015-01-05 07:49:08 -08002110}
2111
msarettc573a402016-08-02 08:05:56 -07002112void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2113 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002114 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002115 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002116 return;
2117 }
msarett71df2d72016-09-30 12:41:42 -07002118
2119 SkIRect bounds;
2120 Lattice latticePlusBounds = lattice;
2121 if (!latticePlusBounds.fBounds) {
2122 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2123 latticePlusBounds.fBounds = &bounds;
2124 }
2125
2126 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002127 LatticePaint latticePaint(paint);
2128 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002129 } else {
msarett16882062016-08-16 09:31:08 -07002130 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002131 }
msarettc573a402016-08-02 08:05:56 -07002132}
2133
reed71c3c762015-06-24 10:29:17 -07002134void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002135 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002136 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002137 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002138 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002139 if (count <= 0) {
2140 return;
2141 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002142 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002143 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002144 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002145}
2146
reedf70b5312016-03-04 16:36:20 -08002147void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002148 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002149 if (key) {
2150 this->onDrawAnnotation(rect, key, value);
2151 }
2152}
2153
reede47829b2015-08-06 10:02:53 -07002154void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2155 const SkPaint* paint, SrcRectConstraint constraint) {
2156 if (src) {
2157 this->drawImageRect(image, *src, dst, paint, constraint);
2158 } else {
2159 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2160 dst, paint, constraint);
2161 }
2162}
2163void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2164 const SkPaint* paint, SrcRectConstraint constraint) {
2165 if (src) {
2166 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2167 } else {
2168 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2169 dst, paint, constraint);
2170 }
2171}
2172
Mike Reed4204da22017-05-17 08:53:36 -04002173void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002174 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002175 this->onDrawShadowRec(path, rec);
2176}
2177
2178void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2179 SkPaint paint;
2180 const SkRect& pathBounds = path.getBounds();
2181
Mike Reed38992392019-07-30 10:48:15 -04002182 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002183 while (iter.next()) {
2184 iter.fDevice->drawShadow(path, rec);
2185 }
Mike Reed38992392019-07-30 10:48:15 -04002186 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002187}
2188
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002189void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002190 QuadAAFlags aaFlags, const SkColor4f& color,
2191 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002192 TRACE_EVENT0("skia", TRACE_FUNC);
2193 // Make sure the rect is sorted before passing it along
2194 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2195}
2196
2197void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2198 const SkPoint dstClips[],
2199 const SkMatrix preViewMatrices[],
2200 const SkPaint* paint,
2201 SrcRectConstraint constraint) {
2202 TRACE_EVENT0("skia", TRACE_FUNC);
2203 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2204}
2205
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206//////////////////////////////////////////////////////////////////////////////
2207// These are the virtual drawing methods
2208//////////////////////////////////////////////////////////////////////////////
2209
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002210void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002211 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002212 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2213 }
2214}
2215
reed41af9662015-01-05 07:49:08 -08002216void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002217 this->internalDrawPaint(paint);
2218}
2219
2220void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002221 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222
2223 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002224 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225 }
2226
Mike Reed38992392019-07-30 10:48:15 -04002227 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002228}
2229
reed41af9662015-01-05 07:49:08 -08002230void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2231 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232 if ((long)count <= 0) {
2233 return;
2234 }
2235
Mike Reed822128b2017-02-28 16:41:03 -05002236 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002237 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002238 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002239 // special-case 2 points (common for drawing a single line)
2240 if (2 == count) {
2241 r.set(pts[0], pts[1]);
2242 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002243 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002244 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002245 if (!r.isFinite()) {
2246 return;
2247 }
Mike Reed822128b2017-02-28 16:41:03 -05002248 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002249 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2250 return;
2251 }
2252 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002253 }
reed@google.coma584aed2012-05-16 14:06:02 +00002254
halcanary96fcdcc2015-08-27 07:41:13 -07002255 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256
Mike Reed38992392019-07-30 10:48:15 -04002257 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002258
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002260 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 }
reed@google.com4b226022011-01-11 18:32:13 +00002262
Mike Reed38992392019-07-30 10:48:15 -04002263 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264}
2265
reed4a167172016-08-18 17:15:25 -07002266static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002267 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002268}
2269
reed41af9662015-01-05 07:49:08 -08002270void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002271 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002273 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002274 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002275 return;
2276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 }
reed@google.com4b226022011-01-11 18:32:13 +00002278
reed4a167172016-08-18 17:15:25 -07002279 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002280 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281
reed4a167172016-08-18 17:15:25 -07002282 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002283 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002284 }
2285
Mike Reed38992392019-07-30 10:48:15 -04002286 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002287 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002288 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002289 SkDrawIter iter(this);
2290 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002291 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294}
2295
msarett44df6512016-08-25 13:54:30 -07002296void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002297 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002298 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002299 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002300 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2301 return;
2302 }
msarett44df6512016-08-25 13:54:30 -07002303 }
2304
Mike Reed38992392019-07-30 10:48:15 -04002305 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002306
2307 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002308 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002309 }
2310
Mike Reed38992392019-07-30 10:48:15 -04002311 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002312}
2313
Mike Reedd5674082019-04-19 15:00:47 -04002314void SkCanvas::onDrawBehind(const SkPaint& paint) {
2315 SkIRect bounds;
2316 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2317 for (;;) {
2318 const MCRec* rec = (const MCRec*)iter.prev();
2319 if (!rec) {
2320 return; // no backimages, so nothing to draw
2321 }
2322 if (rec->fBackImage) {
2323 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2324 rec->fBackImage->fImage->width(),
2325 rec->fBackImage->fImage->height());
2326 break;
2327 }
2328 }
2329
Mike Reed38992392019-07-30 10:48:15 -04002330 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002331
2332 while (iter.next()) {
2333 SkBaseDevice* dev = iter.fDevice;
2334
Mike Reedd5674082019-04-19 15:00:47 -04002335 dev->save();
2336 // We use clipRegion because it is already defined to operate in dev-space
2337 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2338 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002339 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002340 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002341 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002342 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002343 }
2344
Mike Reed38992392019-07-30 10:48:15 -04002345 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002346}
2347
reed41af9662015-01-05 07:49:08 -08002348void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002349 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002350 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002351 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002352 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002353 return;
2354 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002355 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002356
Mike Reed38992392019-07-30 10:48:15 -04002357 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002358
2359 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002360 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002361 }
2362
Mike Reed38992392019-07-30 10:48:15 -04002363 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002364}
2365
bsalomonac3aa242016-08-19 11:25:19 -07002366void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2367 SkScalar sweepAngle, bool useCenter,
2368 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002369 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002370 if (paint.canComputeFastBounds()) {
2371 SkRect storage;
2372 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002373 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002374 return;
2375 }
bsalomonac3aa242016-08-19 11:25:19 -07002376 }
2377
Mike Reed38992392019-07-30 10:48:15 -04002378 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002379
2380 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002381 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002382 }
2383
Mike Reed38992392019-07-30 10:48:15 -04002384 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002385}
2386
reed41af9662015-01-05 07:49:08 -08002387void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002388 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002389 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002390 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2391 return;
2392 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002393 }
2394
2395 if (rrect.isRect()) {
2396 // call the non-virtual version
2397 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002398 return;
2399 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002400 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002401 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2402 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002403 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002404
Mike Reed38992392019-07-30 10:48:15 -04002405 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002406
2407 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002408 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002409 }
2410
Mike Reed38992392019-07-30 10:48:15 -04002411 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002412}
2413
Mike Reed822128b2017-02-28 16:41:03 -05002414void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002415 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002416 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002417 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2418 return;
2419 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002420 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002421
Mike Reed38992392019-07-30 10:48:15 -04002422 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002423
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002424 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002425 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002426 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002427
Mike Reed38992392019-07-30 10:48:15 -04002428 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002429}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002430
reed41af9662015-01-05 07:49:08 -08002431void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002432 if (!path.isFinite()) {
2433 return;
2434 }
2435
Mike Reed822128b2017-02-28 16:41:03 -05002436 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002437 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002438 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002439 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2440 return;
2441 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002443
Mike Reed822128b2017-02-28 16:41:03 -05002444 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002445 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002446 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002447 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002448 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450
Mike Reed38992392019-07-30 10:48:15 -04002451 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002452
2453 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002454 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002455 }
2456
Mike Reed38992392019-07-30 10:48:15 -04002457 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002458}
2459
reed262a71b2015-12-05 13:07:27 -08002460bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002461 if (!paint.getImageFilter()) {
2462 return false;
2463 }
2464
2465 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002466 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002467 return false;
2468 }
2469
2470 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2471 // Once we can filter and the filter will return a result larger than itself, we should be
2472 // able to remove this constraint.
2473 // skbug.com/4526
2474 //
2475 SkPoint pt;
2476 ctm.mapXY(x, y, &pt);
2477 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2478 return ir.contains(fMCRec->fRasterClip.getBounds());
2479}
2480
Mike Reedf441cfc2018-04-11 14:50:16 -04002481// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2482// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2483// null.
2484static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2485 if (paintParam) {
2486 *real = *paintParam;
2487 real->setStyle(SkPaint::kFill_Style);
2488 real->setPathEffect(nullptr);
2489 paintParam = real;
2490 }
2491 return paintParam;
2492}
2493
reeda85d4d02015-05-06 12:56:48 -07002494void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002495 SkPaint realPaint;
2496 paint = init_image_paint(&realPaint, paint);
2497
reeda85d4d02015-05-06 12:56:48 -07002498 SkRect bounds = SkRect::MakeXYWH(x, y,
2499 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002500 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002501 SkRect tmp = bounds;
2502 if (paint) {
2503 paint->computeFastBounds(tmp, &tmp);
2504 }
2505 if (this->quickReject(tmp)) {
2506 return;
2507 }
reeda85d4d02015-05-06 12:56:48 -07002508 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002509 // At this point we need a real paint object. If the caller passed null, then we should
2510 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2511 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2512 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002513
reeda2217ef2016-07-20 06:04:34 -07002514 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002515 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2516 *paint);
2517 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002518 special = this->getDevice()->makeSpecial(image);
2519 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002520 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002521 }
2522 }
2523
Mike Reed38992392019-07-30 10:48:15 -04002524 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002525
reeda85d4d02015-05-06 12:56:48 -07002526 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002527 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002528 if (special) {
2529 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002530 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002531 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002532 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002533 SkScalarRoundToInt(pt.fY), pnt,
2534 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002535 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002536 iter.fDevice->drawImageRect(
2537 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2538 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002539 }
reeda85d4d02015-05-06 12:56:48 -07002540 }
halcanary9d524f22016-03-29 09:03:52 -07002541
Mike Reed38992392019-07-30 10:48:15 -04002542 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002543}
2544
reed41af9662015-01-05 07:49:08 -08002545void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002546 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002547 SkPaint realPaint;
2548 paint = init_image_paint(&realPaint, paint);
2549
halcanary96fcdcc2015-08-27 07:41:13 -07002550 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002551 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002552 if (paint) {
2553 paint->computeFastBounds(dst, &storage);
2554 }
2555 if (this->quickReject(storage)) {
2556 return;
2557 }
reeda85d4d02015-05-06 12:56:48 -07002558 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002559 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002560
Mike Reed38992392019-07-30 10:48:15 -04002561 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002562
reeda85d4d02015-05-06 12:56:48 -07002563 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002564 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002565 }
halcanary9d524f22016-03-29 09:03:52 -07002566
Mike Reed38992392019-07-30 10:48:15 -04002567 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002568}
2569
reed41af9662015-01-05 07:49:08 -08002570void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571 SkDEBUGCODE(bitmap.validate();)
2572
reed33366972015-10-08 09:22:02 -07002573 if (bitmap.drawsNothing()) {
2574 return;
2575 }
2576
Mike Reedf441cfc2018-04-11 14:50:16 -04002577 SkPaint realPaint;
2578 init_image_paint(&realPaint, paint);
2579 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002580
Mike Reed822128b2017-02-28 16:41:03 -05002581 SkRect bounds;
2582 bitmap.getBounds(&bounds);
2583 bounds.offset(x, y);
2584 bool canFastBounds = paint->canComputeFastBounds();
2585 if (canFastBounds) {
2586 SkRect storage;
2587 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002588 return;
2589 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002590 }
reed@google.com4b226022011-01-11 18:32:13 +00002591
reeda2217ef2016-07-20 06:04:34 -07002592 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002593 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2594 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002595 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002596 special = this->getDevice()->makeSpecial(bitmap);
2597 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002598 drawAsSprite = false;
2599 }
2600 }
2601
Mike Reed38992392019-07-30 10:48:15 -04002602 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002603
2604 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002605 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002606 if (special) {
reed262a71b2015-12-05 13:07:27 -08002607 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002608 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002609 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002610 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002611 SkScalarRoundToInt(pt.fY), pnt,
2612 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002613 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002614 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2615 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2616 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002617 }
reed33366972015-10-08 09:22:02 -07002618 }
msarettfbfa2582016-08-12 08:29:08 -07002619
Mike Reed38992392019-07-30 10:48:15 -04002620 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002621}
2622
reed@google.com9987ec32011-09-07 11:57:52 +00002623// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002624void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002625 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002626 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002627 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002628 return;
2629 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002630
halcanary96fcdcc2015-08-27 07:41:13 -07002631 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002632 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002633 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2634 return;
2635 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002636 }
reed@google.com3d608122011-11-21 15:16:16 +00002637
reed@google.com33535f32012-09-25 15:37:50 +00002638 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002639 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002640 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002641 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002642
Mike Reed38992392019-07-30 10:48:15 -04002643 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002644
reed@google.com33535f32012-09-25 15:37:50 +00002645 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002646 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002647 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002648
Mike Reed38992392019-07-30 10:48:15 -04002649 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002650}
2651
reed41af9662015-01-05 07:49:08 -08002652void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002653 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002654 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002655 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002656}
2657
reed4c21dc52015-06-25 12:32:03 -07002658void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2659 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002660 SkPaint realPaint;
2661 paint = init_image_paint(&realPaint, paint);
2662
halcanary96fcdcc2015-08-27 07:41:13 -07002663 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002664 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002665 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2666 return;
2667 }
reed@google.com3d608122011-11-21 15:16:16 +00002668 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002669 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002670
Mike Reed38992392019-07-30 10:48:15 -04002671 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002672
reed4c21dc52015-06-25 12:32:03 -07002673 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002674 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002675 }
halcanary9d524f22016-03-29 09:03:52 -07002676
Mike Reed38992392019-07-30 10:48:15 -04002677 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002678}
2679
reed41af9662015-01-05 07:49:08 -08002680void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2681 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002682 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002683 SkPaint realPaint;
2684 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002685
halcanary96fcdcc2015-08-27 07:41:13 -07002686 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002687 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002688 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2689 return;
2690 }
reed4c21dc52015-06-25 12:32:03 -07002691 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002692 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002693
Mike Reed38992392019-07-30 10:48:15 -04002694 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002695
reed4c21dc52015-06-25 12:32:03 -07002696 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002697 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002698 }
halcanary9d524f22016-03-29 09:03:52 -07002699
Mike Reed38992392019-07-30 10:48:15 -04002700 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002701}
2702
msarett16882062016-08-16 09:31:08 -07002703void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2704 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002705 SkPaint realPaint;
2706 paint = init_image_paint(&realPaint, paint);
2707
msarett16882062016-08-16 09:31:08 -07002708 if (nullptr == paint || paint->canComputeFastBounds()) {
2709 SkRect storage;
2710 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2711 return;
2712 }
2713 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002714 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002715
Mike Reed38992392019-07-30 10:48:15 -04002716 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002717
2718 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002719 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002720 }
2721
Mike Reed38992392019-07-30 10:48:15 -04002722 DRAW_END
msarett16882062016-08-16 09:31:08 -07002723}
2724
2725void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2726 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002727 SkPaint realPaint;
2728 paint = init_image_paint(&realPaint, paint);
2729
msarett16882062016-08-16 09:31:08 -07002730 if (nullptr == paint || paint->canComputeFastBounds()) {
2731 SkRect storage;
2732 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2733 return;
2734 }
2735 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002736 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002737
Mike Reed38992392019-07-30 10:48:15 -04002738 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002739
2740 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002741 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002742 }
2743
Mike Reed38992392019-07-30 10:48:15 -04002744 DRAW_END
msarett16882062016-08-16 09:31:08 -07002745}
2746
fmalita00d5c2c2014-08-21 08:53:26 -07002747void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2748 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002749 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002750 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002751 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002752 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002753 SkRect tmp;
2754 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2755 return;
2756 }
2757 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002758 }
2759
fmalita024f9962015-03-03 19:08:17 -08002760 // We cannot filter in the looper as we normally do, because the paint is
2761 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002762 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002763
fmalitaaa1b9122014-08-28 14:32:24 -07002764 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002765 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002766 }
2767
Mike Reed38992392019-07-30 10:48:15 -04002768 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002769}
2770
Mike Reed358fcad2018-11-23 15:27:51 -05002771// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002772void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002773 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2774 TRACE_EVENT0("skia", TRACE_FUNC);
2775 if (byteLength) {
2776 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002777 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002778 }
2779}
Mike Reed4f81bb72019-01-23 09:23:00 -05002780
fmalita00d5c2c2014-08-21 08:53:26 -07002781void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2782 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002783 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002784 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002785 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002786 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002787}
reed@google.come0d9ce82014-04-23 04:00:17 +00002788
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002789#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002790void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002791 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002792 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002793
2794 while (iter.next()) {
2795 // In the common case of one iteration we could std::move vertices here.
Mike Reed5caf9352020-03-02 14:57:09 -05002796 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002797 }
2798
Mike Reed38992392019-07-30 10:48:15 -04002799 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002800}
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002801#else
2802void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2803 const SkPaint& paint) {
2804 DRAW_BEGIN(paint, nullptr)
2805
2806 while (iter.next()) {
2807 // In the common case of one iteration we could std::move vertices here.
2808 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2809 }
2810
2811 DRAW_END
2812}
2813#endif
Brian Salomon199fb872017-02-06 09:41:10 -05002814
dandovb3c9d1c2014-08-12 08:34:29 -07002815void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002816 const SkPoint texCoords[4], SkBlendMode bmode,
2817 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002818 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002819 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002820 return;
2821 }
mtklein6cfa73a2014-08-13 13:33:49 -07002822
Mike Reedfaba3712016-11-03 14:45:31 -04002823 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002824}
2825
2826void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002827 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002828 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002829 // Since a patch is always within the convex hull of the control points, we discard it when its
2830 // bounding rectangle is completely outside the current clip.
2831 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002832 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002833 if (this->quickReject(bounds)) {
2834 return;
2835 }
mtklein6cfa73a2014-08-13 13:33:49 -07002836
Mike Reed38992392019-07-30 10:48:15 -04002837 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002838
dandovecfff212014-08-04 10:02:00 -07002839 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002840 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002841 }
mtklein6cfa73a2014-08-13 13:33:49 -07002842
Mike Reed38992392019-07-30 10:48:15 -04002843 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002844}
2845
reeda8db7282015-07-07 10:22:31 -07002846void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002847#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002848 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002849#endif
reede3b38ce2016-01-08 09:18:44 -08002850 RETURN_ON_NULL(dr);
2851 if (x || y) {
2852 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2853 this->onDrawDrawable(dr, &matrix);
2854 } else {
2855 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002856 }
2857}
2858
reeda8db7282015-07-07 10:22:31 -07002859void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002860#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002861 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002862#endif
reede3b38ce2016-01-08 09:18:44 -08002863 RETURN_ON_NULL(dr);
2864 if (matrix && matrix->isIdentity()) {
2865 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002866 }
reede3b38ce2016-01-08 09:18:44 -08002867 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002868}
2869
2870void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002871 // drawable bounds are no longer reliable (e.g. android displaylist)
2872 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002873 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002874}
2875
reed71c3c762015-06-24 10:29:17 -07002876void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002877 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002878 const SkRect* cull, const SkPaint* paint) {
2879 if (cull && this->quickReject(*cull)) {
2880 return;
2881 }
2882
2883 SkPaint pnt;
2884 if (paint) {
2885 pnt = *paint;
2886 }
halcanary9d524f22016-03-29 09:03:52 -07002887
Mike Reed38992392019-07-30 10:48:15 -04002888 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002889 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002890 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002891 }
Mike Reed38992392019-07-30 10:48:15 -04002892 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002893}
2894
reedf70b5312016-03-04 16:36:20 -08002895void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2896 SkASSERT(key);
2897
2898 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002899 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002900 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002901 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002902 }
Mike Reed38992392019-07-30 10:48:15 -04002903 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002904}
2905
Michael Ludwiga595f862019-08-27 15:25:49 -04002906void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2907 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002908 SkASSERT(r.isSorted());
2909
2910 // If this used a paint, it would be a filled color with blend mode, which does not
2911 // need to use an autodraw loop, so use SkDrawIter directly.
2912 if (this->quickReject(r)) {
2913 return;
2914 }
2915
Michael Ludwiga4b44882019-08-28 14:34:58 -04002916 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002917 SkDrawIter iter(this);
2918 while(iter.next()) {
2919 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2920 }
2921}
2922
2923void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2924 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2925 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002926 if (count <= 0) {
2927 // Nothing to draw
2928 return;
2929 }
2930
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002931 SkPaint realPaint;
2932 init_image_paint(&realPaint, paint);
2933
Michael Ludwiga4b44882019-08-28 14:34:58 -04002934 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2935 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2936 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2937 // or we need it for the autolooper (since it greatly improves image filter perf).
2938 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2939 bool setBoundsValid = count == 1 || needsAutoLooper;
2940 SkRect setBounds = imageSet[0].fDstRect;
2941 if (imageSet[0].fMatrixIndex >= 0) {
2942 // Account for the per-entry transform that is applied prior to the CTM when drawing
2943 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002944 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002945 if (needsAutoLooper) {
2946 for (int i = 1; i < count; ++i) {
2947 SkRect entryBounds = imageSet[i].fDstRect;
2948 if (imageSet[i].fMatrixIndex >= 0) {
2949 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2950 }
2951 setBounds.joinPossiblyEmptyRect(entryBounds);
2952 }
2953 }
2954
2955 // If we happen to have the draw bounds, though, might as well check quickReject().
2956 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2957 SkRect tmp;
2958 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2959 return;
2960 }
2961 }
2962
2963 if (needsAutoLooper) {
2964 SkASSERT(setBoundsValid);
2965 DRAW_BEGIN(realPaint, &setBounds)
2966 while (iter.next()) {
2967 iter.fDevice->drawEdgeAAImageSet(
2968 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2969 }
2970 DRAW_END
2971 } else {
2972 this->predrawNotify();
2973 SkDrawIter iter(this);
2974 while(iter.next()) {
2975 iter.fDevice->drawEdgeAAImageSet(
2976 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2977 }
2978 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002979}
2980
reed@android.com8a1c16f2008-12-17 15:59:43 +00002981//////////////////////////////////////////////////////////////////////////////
2982// These methods are NOT virtual, and therefore must call back into virtual
2983// methods, rather than actually drawing themselves.
2984//////////////////////////////////////////////////////////////////////////////
2985
reed374772b2016-10-05 17:33:02 -07002986void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002987 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002988 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002989 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002990 this->drawPaint(paint);
2991}
2992
2993void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002994 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002995 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2996}
2997
Mike Reed3661bc92017-02-22 13:21:42 -05002998void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002999 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000 pts[0].set(x0, y0);
3001 pts[1].set(x1, y1);
3002 this->drawPoints(kLines_PointMode, 2, pts, paint);
3003}
3004
Mike Reed3661bc92017-02-22 13:21:42 -05003005void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003006 if (radius < 0) {
3007 radius = 0;
3008 }
3009
3010 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04003011 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003012 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003013}
3014
3015void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3016 const SkPaint& paint) {
3017 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003018 SkRRect rrect;
3019 rrect.setRectXY(r, rx, ry);
3020 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021 } else {
3022 this->drawRect(r, paint);
3023 }
3024}
3025
reed@android.com8a1c16f2008-12-17 15:59:43 +00003026void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3027 SkScalar sweepAngle, bool useCenter,
3028 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04003029 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07003030 if (oval.isEmpty() || !sweepAngle) {
3031 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003032 }
bsalomon21af9ca2016-08-25 12:29:23 -07003033 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003034}
3035
reed@android.comf76bacf2009-05-13 14:00:33 +00003036///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003037#ifdef SK_DISABLE_SKPICTURE
3038void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07003039
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003040
3041void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3042 const SkPaint* paint) {}
3043#else
Mike Klein88d90712018-01-27 17:30:04 +00003044/**
3045 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3046 * against the playback cost of recursing into the subpicture to get at its actual ops.
3047 *
3048 * For now we pick a conservatively small value, though measurement (and other heuristics like
3049 * the type of ops contained) may justify changing this value.
3050 */
3051#define kMaxPictureOpsToUnrollInsteadOfRef 1
3052
reedd5fa1a42014-08-09 11:08:05 -07003053void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04003054 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08003055 RETURN_ON_NULL(picture);
3056
reede3b38ce2016-01-08 09:18:44 -08003057 if (matrix && matrix->isIdentity()) {
3058 matrix = nullptr;
3059 }
Mike Klein88d90712018-01-27 17:30:04 +00003060 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3061 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3062 picture->playback(this);
3063 } else {
3064 this->onDrawPicture(picture, matrix, paint);
3065 }
reedd5fa1a42014-08-09 11:08:05 -07003066}
robertphillips9b14f262014-06-04 05:40:44 -07003067
reedd5fa1a42014-08-09 11:08:05 -07003068void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3069 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003070 if (!paint || paint->canComputeFastBounds()) {
3071 SkRect bounds = picture->cullRect();
3072 if (paint) {
3073 paint->computeFastBounds(bounds, &bounds);
3074 }
3075 if (matrix) {
3076 matrix->mapRect(&bounds);
3077 }
3078 if (this->quickReject(bounds)) {
3079 return;
3080 }
3081 }
3082
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003083 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003084 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003085}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003086#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003087
reed@android.com8a1c16f2008-12-17 15:59:43 +00003088///////////////////////////////////////////////////////////////////////////////
3089///////////////////////////////////////////////////////////////////////////////
3090
reed3aafe112016-08-18 12:45:34 -07003091SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003092 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003093
3094 SkASSERT(canvas);
3095
reed3aafe112016-08-18 12:45:34 -07003096 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003097 fDone = !fImpl->next();
3098}
3099
3100SkCanvas::LayerIter::~LayerIter() {
3101 fImpl->~SkDrawIter();
3102}
3103
3104void SkCanvas::LayerIter::next() {
3105 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003106 if (!fDone) {
3107 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
3108 // filters, so its devices will always be able to report the origin exactly.
3109 fDeviceOrigin = fImpl->fDevice->getOrigin();
3110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003111}
3112
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003113SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003114 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003115}
3116
3117const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003118 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003119}
3120
3121const SkPaint& SkCanvas::LayerIter::paint() const {
3122 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003123 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003124 paint = &fDefaultPaint;
3125 }
3126 return *paint;
3127}
3128
Mike Reedca37f322018-03-08 13:22:16 -05003129SkIRect SkCanvas::LayerIter::clipBounds() const {
3130 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003131}
3132
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003133int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
3134int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003135
3136///////////////////////////////////////////////////////////////////////////////
3137
Brian Osmane8a98632019-04-10 10:26:10 -04003138SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3139SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3140SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3141SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3142
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003143SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3144 const SkRect& dstRect, int matrixIndex, float alpha,
3145 unsigned aaFlags, bool hasClip)
3146 : fImage(std::move(image))
3147 , fSrcRect(srcRect)
3148 , fDstRect(dstRect)
3149 , fMatrixIndex(matrixIndex)
3150 , fAlpha(alpha)
3151 , fAAFlags(aaFlags)
3152 , fHasClip(hasClip) {}
3153
3154SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3155 const SkRect& dstRect, float alpha, unsigned aaFlags)
3156 : fImage(std::move(image))
3157 , fSrcRect(srcRect)
3158 , fDstRect(dstRect)
3159 , fAlpha(alpha)
3160 , fAAFlags(aaFlags) {}
3161
3162///////////////////////////////////////////////////////////////////////////////
3163
Mike Reed5df49342016-11-12 08:06:55 -06003164std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003165 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003166 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003167 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003168 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003169
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003170 SkBitmap bitmap;
3171 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003172 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003173 }
Mike Reed12f77342017-11-08 11:19:52 -05003174
3175 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003176 std::make_unique<SkCanvas>(bitmap, *props) :
3177 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003178}
reedd5fa1a42014-08-09 11:08:05 -07003179
3180///////////////////////////////////////////////////////////////////////////////
3181
Florin Malitaee424ac2016-12-01 12:47:59 -05003182SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003183 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003184
Florin Malita439ace92016-12-02 12:05:41 -05003185SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003186 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003187
Herb Derbyefe39bc2018-05-01 17:06:20 -04003188SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003189 : INHERITED(device) {}
3190
Florin Malitaee424ac2016-12-01 12:47:59 -05003191SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3192 (void)this->INHERITED::getSaveLayerStrategy(rec);
3193 return kNoLayer_SaveLayerStrategy;
3194}
3195
Mike Reed148b7fd2018-12-18 17:38:18 -05003196bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3197 return false;
3198}
3199
Florin Malitaee424ac2016-12-01 12:47:59 -05003200///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003201
reed73603f32016-09-20 08:42:38 -07003202static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3203static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3204static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3205static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3206static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3207static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003208
3209///////////////////////////////////////////////////////////////////////////////////////////////////
3210
3211SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3212 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003213 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003214 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003215 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003216 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003217 clip.setEmpty();
3218 }
3219
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003220 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003221 return handle;
3222 }
3223 return nullptr;
3224}
3225
3226static bool install(SkBitmap* bm, const SkImageInfo& info,
3227 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003228 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003229}
3230
3231SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3232 SkBitmap* bm) {
3233 SkRasterHandleAllocator::Rec rec;
3234 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3235 return nullptr;
3236 }
3237 return rec.fHandle;
3238}
3239
3240std::unique_ptr<SkCanvas>
3241SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3242 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003243 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003244 return nullptr;
3245 }
3246
3247 SkBitmap bm;
3248 Handle hndl;
3249
3250 if (rec) {
3251 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3252 } else {
3253 hndl = alloc->allocBitmap(info, &bm);
3254 }
3255 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3256}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003257
3258///////////////////////////////////////////////////////////////////////////////////////////////////