blob: 17c8a276d6a7a7fe582c283b02896f87410f633e [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/private/SkNx.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040023#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050025#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050035#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050036#include "src/core/SkMatrixUtils.h"
37#include "src/core/SkPaintPriv.h"
38#include "src/core/SkRasterClip.h"
39#include "src/core/SkSpecialImage.h"
40#include "src/core/SkStrikeCache.h"
41#include "src/core/SkTLazy.h"
42#include "src/core/SkTextFormatParams.h"
43#include "src/core/SkTraceEvent.h"
44#include "src/image/SkImage_Base.h"
45#include "src/image/SkSurface_Base.h"
46#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040047
bungemand3ebb482015-08-05 13:57:49 -070048#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000050#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050051#include "include/gpu/GrContext.h"
52#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000053#endif
54
reede3b38ce2016-01-08 09:18:44 -080055#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050056#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080057
Mike Klein1bb7e232019-12-10 08:58:52 -060058// This is a test: static_assert with no message is a c++17 feature,
59// and std::max() is constexpr only since the c++14 stdlib.
60static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000061
Mike Reed139e5e02017-03-08 11:29:33 -050062///////////////////////////////////////////////////////////////////////////////////////////////////
63
reedc83a2972015-07-16 07:40:45 -070064/*
65 * Return true if the drawing this rect would hit every pixels in the canvas.
66 *
67 * Returns false if
68 * - rect does not contain the canvas' bounds
69 * - paint is not fill
70 * - paint would blur or otherwise change the coverage of the rect
71 */
72bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
73 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070074 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
75 (int)kNone_ShaderOverrideOpacity,
76 "need_matching_enums0");
77 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
78 (int)kOpaque_ShaderOverrideOpacity,
79 "need_matching_enums1");
80 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
81 (int)kNotOpaque_ShaderOverrideOpacity,
82 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070083
84 const SkISize size = this->getBaseLayerSize();
85 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050086
87 // if we're clipped at all, we can't overwrite the entire surface
88 {
89 SkBaseDevice* base = this->getDevice();
90 SkBaseDevice* top = this->getTopDevice();
91 if (base != top) {
92 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
93 }
94 if (!base->clipIsWideOpen()) {
95 return false;
96 }
reedc83a2972015-07-16 07:40:45 -070097 }
98
99 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700100 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700101 return false; // conservative
102 }
halcanaryc5769b22016-08-10 07:13:21 -0700103
104 SkRect devRect;
105 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
106 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700107 return false;
108 }
109 }
110
111 if (paint) {
112 SkPaint::Style paintStyle = paint->getStyle();
113 if (!(paintStyle == SkPaint::kFill_Style ||
114 paintStyle == SkPaint::kStrokeAndFill_Style)) {
115 return false;
116 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400117 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700118 return false; // conservative
119 }
120 }
121 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
122}
123
124///////////////////////////////////////////////////////////////////////////////////////////////////
125
reed@google.comda17f752012-08-16 18:27:05 +0000126// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127//#define SK_TRACE_SAVERESTORE
128
129#ifdef SK_TRACE_SAVERESTORE
130 static int gLayerCounter;
131 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
132 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
133
134 static int gRecCounter;
135 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
136 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
137
138 static int gCanvasCounter;
139 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
140 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
141#else
142 #define inc_layer()
143 #define dec_layer()
144 #define inc_rec()
145 #define dec_rec()
146 #define inc_canvas()
147 #define dec_canvas()
148#endif
149
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000150typedef SkTLazy<SkPaint> SkLazyPaint;
151
reedc83a2972015-07-16 07:40:45 -0700152void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000153 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700154 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
155 ? SkSurface::kDiscard_ContentChangeMode
156 : SkSurface::kRetain_ContentChangeMode);
157 }
158}
159
160void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
161 ShaderOverrideOpacity overrideOpacity) {
162 if (fSurfaceBase) {
163 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
164 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
165 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
166 // and therefore we don't care which mode we're in.
167 //
168 if (fSurfaceBase->outstandingImageSnapshot()) {
169 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
170 mode = SkSurface::kDiscard_ContentChangeMode;
171 }
172 }
173 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000174 }
175}
176
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000179/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 The clip/matrix/proc are fields that reflect the top of the save/restore
181 stack. Whenever the canvas changes, it marks a dirty flag, and then before
182 these are used (assuming we're not on a layer) we rebuild these cache
183 values: they reflect the top of the save stack, but translated and clipped
184 by the device's XY offset and bitmap-bounds.
185*/
186struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400187 DeviceCM* fNext;
188 sk_sp<SkBaseDevice> fDevice;
189 SkRasterClip fClip;
190 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
191 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400192 sk_sp<SkImage> fClipImage;
193 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
Florin Malita53f77bd2017-04-28 13:48:37 -0400195 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000196 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700197 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400198 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500199 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700200 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000201 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400202 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400203 {}
reed@google.com4b226022011-01-11 18:32:13 +0000204
mtkleinfeaadee2015-04-08 11:25:48 -0700205 void reset(const SkIRect& bounds) {
206 SkASSERT(!fPaint);
207 SkASSERT(!fNext);
208 SkASSERT(fDevice);
209 fClip.setRect(bounds);
210 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211};
212
Mike Reed148b7fd2018-12-18 17:38:18 -0500213namespace {
214// Encapsulate state needed to restore from saveBehind()
215struct BackImage {
216 sk_sp<SkSpecialImage> fImage;
217 SkIPoint fLoc;
218};
219}
220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221/* This is the record we keep for each save/restore level in the stack.
222 Since a level optionally copies the matrix and/or stack, we have pointers
223 for these fields. If the value is copied for this level, the copy is
224 stored in the ...Storage field, and the pointer points to that. If the
225 value is not copied for this level, we ignore ...Storage, and just point
226 at the corresponding value in the previous level in the stack.
227*/
228class SkCanvas::MCRec {
229public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500230 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 /* If there are any layers in the stack, this points to the top-most
232 one that is at or below this level in the stack (so we know what
233 bitmap/device to draw into from this level. This value is NOT
234 reference counted, since the real owner is either our fLayer field,
235 or a previous one in a lower level.)
236 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500237 DeviceCM* fTopLayer;
238 std::unique_ptr<BackImage> fBackImage;
239 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500240 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500241 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
Mike Reeda1361362017-03-07 09:37:29 -0500243 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700244 fLayer = nullptr;
245 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800246 fMatrix.reset();
247 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700248
reedd9544982014-09-09 18:46:22 -0700249 // don't bother initializing fNext
250 inc_rec();
251 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400252 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700253 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700254 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800255 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 // don't bother initializing fNext
258 inc_rec();
259 }
260 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700261 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 dec_rec();
263 }
mtkleinfeaadee2015-04-08 11:25:48 -0700264
265 void reset(const SkIRect& bounds) {
266 SkASSERT(fLayer);
267 SkASSERT(fDeferredSaveCount == 0);
268
269 fMatrix.reset();
270 fRasterClip.setRect(bounds);
271 fLayer->reset(bounds);
272 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
Mike Reeda1361362017-03-07 09:37:29 -0500275class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276public:
Mike Reeda1361362017-03-07 09:37:29 -0500277 SkDrawIter(SkCanvas* canvas)
278 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
279 {}
reed@google.com4b226022011-01-11 18:32:13 +0000280
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000282 const DeviceCM* rec = fCurrLayer;
283 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400284 fDevice = rec->fDevice.get();
285 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700287 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 return true;
289 }
290 return false;
291 }
reed@google.com4b226022011-01-11 18:32:13 +0000292
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000294
Mike Reed99330ba2017-02-22 11:01:08 -0500295 SkBaseDevice* fDevice;
296
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 const DeviceCM* fCurrLayer;
299 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300};
301
Florin Malita713b8ef2017-04-28 10:57:24 -0400302#define FOR_EACH_TOP_DEVICE( code ) \
303 do { \
304 DeviceCM* layer = fMCRec->fTopLayer; \
305 while (layer) { \
306 SkBaseDevice* device = layer->fDevice.get(); \
307 if (device) { \
308 code; \
309 } \
310 layer = layer->fNext; \
311 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500312 } while (0)
313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314/////////////////////////////////////////////////////////////////////////////
315
reeddbc3cef2015-04-29 12:18:57 -0700316/**
317 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700318 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700319 */
reedd053ce92016-03-22 10:17:23 -0700320static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700321 SkImageFilter* imgf = paint.getImageFilter();
322 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700323 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700324 }
325
reedd053ce92016-03-22 10:17:23 -0700326 SkColorFilter* imgCFPtr;
327 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700328 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700329 }
reedd053ce92016-03-22 10:17:23 -0700330 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700331
332 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700333 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700334 // there is no existing paint colorfilter, so we can just return the imagefilter's
335 return imgCF;
336 }
337
338 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
339 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500340 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700341}
342
senorblanco87e066e2015-10-28 11:23:36 -0700343/**
344 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
345 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
346 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
347 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
348 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
349 * conservative "effective" bounds based on the settings in the paint... with one exception. This
350 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
351 * deliberately ignored.
352 */
353static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
354 const SkRect& rawBounds,
355 SkRect* storage) {
356 SkPaint tmpUnfiltered(paint);
357 tmpUnfiltered.setImageFilter(nullptr);
358 if (tmpUnfiltered.canComputeFastBounds()) {
359 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
360 } else {
361 return rawBounds;
362 }
363}
364
Mike Reed38992392019-07-30 10:48:15 -0400365class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366public:
senorblanco87e066e2015-10-28 11:23:36 -0700367 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
368 // paint. It's used to determine the size of the offscreen layer for filters.
369 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400370 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
371 bool skipLayerForImageFilter = false,
372 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400374 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700376 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
Mike Reed38992392019-07-30 10:48:15 -0400378 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
379 SkASSERT(!fLazyPaint.isValid());
380 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700381 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700382 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700383 fPaint = paint;
384 }
385
386 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700387 /**
388 * We implement ImageFilters for a given draw by creating a layer, then applying the
389 * imagefilter to the pixels of that layer (its backing surface/image), and then
390 * we call restore() to xfer that layer to the main canvas.
391 *
392 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
393 * 2. Generate the src pixels:
394 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
395 * return (fPaint). We then draw the primitive (using srcover) into a cleared
396 * buffer/surface.
397 * 3. Restore the layer created in #1
398 * The imagefilter is passed the buffer/surface from the layer (now filled with the
399 * src pixels of the primitive). It returns a new "filtered" buffer, which we
400 * draw onto the previous layer using the xfermode from the original paint.
401 */
Mike Reed38992392019-07-30 10:48:15 -0400402
403 SkPaint restorePaint;
404 restorePaint.setImageFilter(fPaint->refImageFilter());
405 restorePaint.setBlendMode(fPaint->getBlendMode());
406
senorblanco87e066e2015-10-28 11:23:36 -0700407 SkRect storage;
408 if (rawBounds) {
409 // Make rawBounds include all paint outsets except for those due to image filters.
410 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
411 }
Mike Reed38992392019-07-30 10:48:15 -0400412 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700413 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700414 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000415
Mike Reed38992392019-07-30 10:48:15 -0400416 // Remove the restorePaint fields from our "working" paint
417 SkASSERT(!fLazyPaint.isValid());
418 SkPaint* paint = fLazyPaint.set(origPaint);
419 paint->setImageFilter(nullptr);
420 paint->setBlendMode(SkBlendMode::kSrcOver);
421 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000422 }
423 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000424
Mike Reed38992392019-07-30 10:48:15 -0400425 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700426 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000427 fCanvas->internalRestore();
428 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000429 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000431
reed@google.com4e2b3d32011-04-07 14:18:59 +0000432 const SkPaint& paint() const {
433 SkASSERT(fPaint);
434 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437private:
Mike Reed38992392019-07-30 10:48:15 -0400438 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000439 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400440 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700442 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443};
444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445////////// macros to place around the internal draw calls //////////////////
446
Mike Reed38992392019-07-30 10:48:15 -0400447#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700448 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400449 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
450 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800451
452
Mike Reed38992392019-07-30 10:48:15 -0400453#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000454 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400455 AutoLayerForImageFilter draw(this, paint, true); \
456 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000457
Mike Reed38992392019-07-30 10:48:15 -0400458#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000459 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400460 AutoLayerForImageFilter draw(this, paint, false, bounds); \
461 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000462
Mike Reed38992392019-07-30 10:48:15 -0400463#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700464 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400465 AutoLayerForImageFilter draw(this, paint, false, bounds); \
466 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700467
Mike Reed38992392019-07-30 10:48:15 -0400468#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469
470////////////////////////////////////////////////////////////////////////////
471
msarettfbfa2582016-08-12 08:29:08 -0700472static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
473 if (bounds.isEmpty()) {
474 return SkRect::MakeEmpty();
475 }
476
477 // Expand bounds out by 1 in case we are anti-aliasing. We store the
478 // bounds as floats to enable a faster quick reject implementation.
479 SkRect dst;
480 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
481 return dst;
482}
483
mtkleinfeaadee2015-04-08 11:25:48 -0700484void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
485 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700486 fMCRec->reset(bounds);
487
488 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500489 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400490 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700491 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700492 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700493}
494
Hal Canary363a3f82018-10-04 11:04:48 -0400495void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800496 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497
498 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500499 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500500 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700501 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
reeda499f902015-05-01 09:34:31 -0700503 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
504 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400505 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508
halcanary96fcdcc2015-08-27 07:41:13 -0700509 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000510
reedf92c8662014-08-18 08:02:43 -0700511 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700512 // The root device and the canvas should always have the same pixel geometry
513 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800514 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700515 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500516
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700518 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400519
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500520 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521}
522
reed@google.comcde92112011-07-06 20:00:52 +0000523SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000524 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700525 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000527 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000528
Hal Canary363a3f82018-10-04 11:04:48 -0400529 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000530}
531
reed96a857e2015-01-25 10:33:58 -0800532SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800534 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535{
536 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400537 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500538 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700539}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000540
Hal Canary363a3f82018-10-04 11:04:48 -0400541SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700542 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700543 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700544{
545 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700546
Mike Reed566e53c2017-03-10 10:49:45 -0500547 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400548 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700549}
550
Herb Derbyefe39bc2018-05-01 17:06:20 -0400551SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000552 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700553 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700556
Hal Canary363a3f82018-10-04 11:04:48 -0400557 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700558}
559
reed4a8126e2014-09-22 07:29:03 -0700560SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700561 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700562 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700563{
564 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700565
Mike Reed910ca0f2018-04-25 13:04:05 -0400566 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400567 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700568}
reed29c857d2014-09-21 10:25:07 -0700569
Mike Reed356f7c22017-01-10 11:58:39 -0500570SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
571 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700572 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
573 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500574 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700575{
576 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700577
Mike Reed910ca0f2018-04-25 13:04:05 -0400578 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400579 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580}
581
Mike Reed356f7c22017-01-10 11:58:39 -0500582SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
583
Matt Sarett31f99ce2017-04-11 08:46:01 -0400584#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
585SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
586 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
587 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
588 , fAllocator(nullptr)
589{
590 inc_canvas();
591
592 SkBitmap tmp(bitmap);
593 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400594 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400595 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400596}
597#endif
598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599SkCanvas::~SkCanvas() {
600 // free up the contents of our deque
601 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 this->internalRestore(); // restore the last, since we're going away
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 dec_canvas();
606}
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608///////////////////////////////////////////////////////////////////////////////
609
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000610void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700611 this->onFlush();
612}
613
614void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000615 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000616 if (device) {
617 device->flush();
618 }
619}
620
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500621SkSurface* SkCanvas::getSurface() const {
622 return fSurfaceBase;
623}
624
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000625SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000626 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000627 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
628}
629
senorblancoafc7cce2016-02-02 18:44:15 -0800630SkIRect SkCanvas::getTopLayerBounds() const {
631 SkBaseDevice* d = this->getTopDevice();
632 if (!d) {
633 return SkIRect::MakeEmpty();
634 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500635 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800636}
637
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000638SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000640 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400642 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643}
644
Florin Malita0ed3b642017-01-13 16:56:38 +0000645SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400646 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000647}
648
Mike Reed353196f2017-07-21 11:01:18 -0400649bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000650 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400651 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000652}
653
Mike Reed353196f2017-07-21 11:01:18 -0400654bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
655 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400656}
657
658bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
659 SkPixmap pm;
660 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
661}
662
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000663bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400664 SkPixmap pm;
665 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700666 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000667 }
668 return false;
669}
670
Matt Sarett03dd6d52017-01-23 12:15:09 -0500671bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000672 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 SkBaseDevice* device = this->getDevice();
674 if (!device) {
675 return false;
676 }
677
Matt Sarett03dd6d52017-01-23 12:15:09 -0500678 // This check gives us an early out and prevents generation ID churn on the surface.
679 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
680 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400681 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500682 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000683 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684
Matt Sarett03dd6d52017-01-23 12:15:09 -0500685 // Tell our owning surface to bump its generation ID.
686 const bool completeOverwrite =
687 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700688 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700689
Matt Sarett03dd6d52017-01-23 12:15:09 -0500690 // This can still fail, most notably in the case of a invalid color type or alpha type
691 // conversion. We could pull those checks into this function and avoid the unnecessary
692 // generation ID bump. But then we would be performing those checks twice, since they
693 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400694 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000695}
reed@google.com51df9e32010-12-23 19:29:18 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697//////////////////////////////////////////////////////////////////////////////
698
reed2ff1fce2014-12-11 07:07:37 -0800699void SkCanvas::checkForDeferredSave() {
700 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800701 this->doSave();
702 }
703}
704
reedf0090cb2014-11-26 08:55:51 -0800705int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800706#ifdef SK_DEBUG
707 int count = 0;
708 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
709 for (;;) {
710 const MCRec* rec = (const MCRec*)iter.next();
711 if (!rec) {
712 break;
713 }
714 count += 1 + rec->fDeferredSaveCount;
715 }
716 SkASSERT(count == fSaveCount);
717#endif
718 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800719}
720
721int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800722 fSaveCount += 1;
723 fMCRec->fDeferredSaveCount += 1;
724 return this->getSaveCount() - 1; // return our prev value
725}
726
727void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800728 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700729
730 SkASSERT(fMCRec->fDeferredSaveCount > 0);
731 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800732 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800733}
734
Mike Reed00a97642020-01-25 18:42:23 -0500735int SkCanvas::experimental_saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500736 // TODO: add a virtual for this, and update clients (e.g. chrome)
737 int n = this->save();
Mike Reed46f5c5f2020-02-20 15:42:29 -0500738 this->concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500739 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500740 return n;
741}
742
Mike Reed00a97642020-01-25 18:42:23 -0500743int SkCanvas::experimental_saveCamera(const SkScalar projection[16],
744 const SkScalar camera[16]) {
745 SkM44 proj, cam;
746 proj.setColMajor(projection);
747 cam.setColMajor(camera);
748 return this->experimental_saveCamera(proj, cam);
749}
750
reedf0090cb2014-11-26 08:55:51 -0800751void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800752 if (fMCRec->fDeferredSaveCount > 0) {
753 SkASSERT(fSaveCount > 1);
754 fSaveCount -= 1;
755 fMCRec->fDeferredSaveCount -= 1;
756 } else {
757 // check for underflow
758 if (fMCStack.count() > 1) {
759 this->willRestore();
760 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700761 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800762 this->internalRestore();
763 this->didRestore();
764 }
reedf0090cb2014-11-26 08:55:51 -0800765 }
766}
767
768void SkCanvas::restoreToCount(int count) {
769 // sanity check
770 if (count < 1) {
771 count = 1;
772 }
mtkleinf0f14112014-12-12 08:46:25 -0800773
reedf0090cb2014-11-26 08:55:51 -0800774 int n = this->getSaveCount() - count;
775 for (int i = 0; i < n; ++i) {
776 this->restore();
777 }
778}
779
reed2ff1fce2014-12-11 07:07:37 -0800780void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700782 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000784
Mike Reedc42a1cd2017-02-14 14:25:14 -0500785 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786}
787
reed4960eee2015-12-18 07:09:18 -0800788bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400789 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790}
791
reed4960eee2015-12-18 07:09:18 -0800792bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700793 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400794 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
795 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
796 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
797 // filter does not require a decomposed CTM matrix, the filter space and device space are the
798 // same. When it has been decomposed, we want the original image filter node to process the
799 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
800 // longer the original filter, but has the remainder matrix baked into it, and passing in the
801 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
802 // to the original filter node (barring inflation from consecutive calls to mapRect). While
803 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
804 // passing getDeviceClipBounds() to 'imageFilter' is correct.
805 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
806 // be important to more accurately calculate the clip bounds in the layer space for the original
807 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500808 SkIRect clipBounds = this->getDeviceClipBounds();
809 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000811 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000812
reed96e657d2015-03-10 17:30:07 -0700813 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
814
Robert Phillips12078432018-05-17 11:17:39 -0400815 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
816 // If the image filter DAG affects transparent black then we will need to render
817 // out to the clip bounds
818 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000819 }
Robert Phillips12078432018-05-17 11:17:39 -0400820
821 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700822 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700824 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400825 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400827 inputSaveLayerBounds = clipBounds;
828 }
829
830 if (imageFilter) {
831 // expand the clip bounds by the image filter DAG to include extra content that might
832 // be required by the image filters.
833 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
834 SkImageFilter::kReverse_MapDirection,
835 &inputSaveLayerBounds);
836 }
837
838 SkIRect clippedSaveLayerBounds;
839 if (bounds) {
840 // For better or for worse, user bounds currently act as a hard clip on the layer's
841 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
842 clippedSaveLayerBounds = inputSaveLayerBounds;
843 } else {
844 // If there are no user bounds, we don't want to artificially restrict the resulting
845 // layer bounds, so allow the expanded clip bounds free reign.
846 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800848
849 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400850 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800851 if (BoundsAffectsClip(saveLayerFlags)) {
852 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
853 fMCRec->fRasterClip.setEmpty();
854 fDeviceClipBounds.setEmpty();
855 }
856 return false;
857 }
Robert Phillips12078432018-05-17 11:17:39 -0400858 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859
reed4960eee2015-12-18 07:09:18 -0800860 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700861 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400862 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
863 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000864 }
865
866 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400867 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000868 }
Robert Phillips12078432018-05-17 11:17:39 -0400869
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870 return true;
871}
872
reed4960eee2015-12-18 07:09:18 -0800873int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
874 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000875}
876
Cary Clarke041e312018-03-06 13:00:52 -0500877int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700878 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400879 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
880 // no need for the layer (or any of the draws until the matching restore()
881 this->save();
882 this->clipRect({0,0,0,0});
883 } else {
884 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
885 fSaveCount += 1;
886 this->internalSaveLayer(rec, strategy);
887 }
reed4960eee2015-12-18 07:09:18 -0800888 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800889}
890
Mike Reed148b7fd2018-12-18 17:38:18 -0500891int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
892 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
893 // Assuming clips never expand, if the request bounds is outside of the current clip
894 // there is no need to copy/restore the area, so just devolve back to a regular save.
895 this->save();
896 } else {
897 bool doTheWork = this->onDoSaveBehind(bounds);
898 fSaveCount += 1;
899 this->internalSave();
900 if (doTheWork) {
901 this->internalSaveBehind(bounds);
902 }
903 }
904 return this->getSaveCount() - 1;
905}
906
reeda2217ef2016-07-20 06:04:34 -0700907void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500908 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500909 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400910 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
911 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400912 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500913 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
914 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400915 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400916 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
917 // This means that we only have to copy a dst-sized block of pixels out of src and translate
918 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400919 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
920 dstOrigin.y() - src->getOrigin().y(),
921 dst->width(), dst->height());
922 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400923 return;
924 }
925
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400926 auto special = src->snapSpecial(snapBounds);
927 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400928 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
929 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400930 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
931 }
932 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400933 }
934
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400935 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
936 // by the backdrop filter.
937 SkMatrix toRoot, layerMatrix;
938 SkSize scale;
939 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
940 toRoot = SkMatrix::I();
941 layerMatrix = ctm;
942 } else if (ctm.decomposeScale(&scale, &toRoot)) {
943 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
944 } else {
945 // Perspective, for now, do no scaling of the layer itself.
946 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
947 // the matrix, e.g. based on the midpoint of the near/far planes?
948 toRoot = ctm;
949 layerMatrix = SkMatrix::I();
950 }
951
952 // We have to map the dst bounds from the root space into the layer space where filtering will
953 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
954 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
955 // is a safe, conservative estimate.
956 SkMatrix fromRoot;
957 if (!toRoot.invert(&fromRoot)) {
958 return;
959 }
960
961 // This represents what the backdrop filter needs to produce in the layer space, and is sized
962 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
963 SkIRect layerTargetBounds = fromRoot.mapRect(
964 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
965 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
966 // require some extra input pixels.
967 SkIRect layerInputBounds = filter->filterBounds(
968 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
969 &layerTargetBounds);
970
971 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400972 // be the conservative contents required to fill a layerInputBounds-sized surface with the
973 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400974 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
975 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
976 if (!backdropBounds.intersect(srcDevRect)) {
977 return;
978 }
979
980 auto special = src->snapSpecial(backdropBounds);
981 if (!special) {
982 return;
983 }
984
985 SkColorType colorType = src->imageInfo().colorType();
986 if (colorType == kUnknown_SkColorType) {
987 colorType = kRGBA_8888_SkColorType;
988 }
989 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400990
991 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400992 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400993 // Drawing the temporary and final filtered image requires a higher filter quality if the
994 // 'toRoot' transformation is not identity, in order to minimize the impact on already
995 // rendered edges/content.
996 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
997 p.setFilterQuality(kHigh_SkFilterQuality);
998
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400999 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1000 // and stored in a temporary surface, which is then used as the input to the actual filter.
1001 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1002 if (!tmpSurface) {
1003 return;
1004 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001005
1006 auto tmpCanvas = tmpSurface->getCanvas();
1007 tmpCanvas->clear(SK_ColorTRANSPARENT);
1008 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1009 // space, then maps from root space into the layer space, then maps it so the input layer's
1010 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1011 // performed on backdropBounds.
1012 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1013 tmpCanvas->concat(fromRoot);
1014 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001015
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001016 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1017 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1018 special = tmpSurface->makeImageSnapshot();
1019 } else {
1020 // Since there is no extra transform that was done, update the input bounds to reflect
1021 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1022 // was equal to backdropBounds before it was made relative to the src device and cropped.
1023 // When we use the original snapped image directly, just map the update backdrop bounds
1024 // back into the shared layer space
1025 layerInputBounds = backdropBounds;
1026 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001027
1028 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1029 // draw will be 1-1 so there is no need to increase filter quality.
1030 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001031 }
1032
1033 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1034 // layer space. This has to further offset everything so that filter evaluation thinks the
1035 // source image's top left corner is (0, 0).
1036 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1037 // this can be simplified.
1038 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1039 SkMatrix filterCTM = layerMatrix;
1040 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1041 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1042
1043 SkIPoint offset;
1044 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001045 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001046 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1047 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1048 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1049 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050 offset += layerInputBounds.topLeft();
1051
1052 // Manually setting the device's CTM requires accounting for the device's origin.
1053 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001054 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001055 // a global CTM instead of a device CTM.
1056 SkMatrix dstCTM = toRoot;
1057 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001058 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001059
1060 // And because devices don't have a special-image draw function that supports arbitrary
1061 // matrices, we are abusing the asImage() functionality here...
1062 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001063 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001064 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001065 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001066 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1067 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001068 }
robertphillips7354a4b2015-12-16 05:08:27 -08001069}
reed70ee31b2015-12-10 13:44:45 -08001070
Mike Kleine083f7c2018-02-07 12:54:27 -05001071static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001072 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001073 if (prev.bytesPerPixel() <= 4 &&
1074 prev.colorType() != kRGBA_8888_SkColorType &&
1075 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001076 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1077 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1078 ct = kN32_SkColorType;
1079 }
1080 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001081}
1082
reed4960eee2015-12-18 07:09:18 -08001083void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001084 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001085 const SkRect* bounds = rec.fBounds;
reed4960eee2015-12-18 07:09:18 -08001086 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1087
Michael Ludwigeced98b2020-03-03 10:39:41 -05001088 SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint);
1089 // saveLayer ignores mask filters, so force it to null
1090 if (paint.get() && paint->getMaskFilter()) {
1091 paint.writable()->setMaskFilter(nullptr);
1092 }
1093
Mike Reed5532c2a2019-02-23 12:00:32 -05001094 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1095 // regardless of any hint-rect from the caller. skbug.com/8783
1096 if (rec.fBackdrop) {
1097 bounds = nullptr;
1098 }
1099
Michael Ludwigeced98b2020-03-03 10:39:41 -05001100 SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001101 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001102 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001103
reed8c30a812016-04-20 16:36:51 -07001104 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001105 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1106 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1107 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001108 *
1109 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001110 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1111 * if necessary.
1112 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1113 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001114 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
Michael Ludwig08b260c2019-05-17 11:21:53 -04001115 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001116 * of the original imagefilter, and draw that (via drawSprite)
1117 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1118 *
1119 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1120 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1121 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001122 if (imageFilter) {
1123 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001124 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1125 &modifiedCTM);
1126 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001127 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001128 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001129 modifiedRec = fMCRec;
1130 this->internalSetMatrix(modifiedCTM);
Michael Ludwigeced98b2020-03-03 10:39:41 -05001131 imageFilter = modifiedFilter.get();
1132 paint.writable()->setImageFilter(std::move(modifiedFilter));
Michael Ludwig08b260c2019-05-17 11:21:53 -04001133 }
1134 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1135 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001136 }
reed8c30a812016-04-20 16:36:51 -07001137
junov@chromium.orga907ac32012-02-24 21:54:07 +00001138 // do this before we create the layer. We don't call the public save() since
1139 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001140 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001141
junov@chromium.orga907ac32012-02-24 21:54:07 +00001142 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001143 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001144 if (modifiedRec) {
1145 // In this case there will be no layer in which to stash the matrix so we need to
1146 // revert the prior MCRec to its earlier state.
1147 modifiedRec->fMatrix = stashedMatrix;
1148 }
reed2ff1fce2014-12-11 07:07:37 -08001149 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 }
1151
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001152 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1153 // the clipRectBounds() call above?
1154 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001155 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001156 }
1157
reed8dc0ccb2015-03-20 06:32:52 -07001158 SkPixelGeometry geo = fProps.pixelGeometry();
1159 if (paint) {
reed76033be2015-03-14 10:54:31 -07001160 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001161 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001162 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001163 }
1164 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165
robertphillips5139e502016-07-19 05:10:40 -07001166 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001167 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001168 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001169 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001170 }
reedb2db8982014-11-13 12:41:02 -08001171
Mike Kleine083f7c2018-02-07 12:54:27 -05001172 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001173 if (rec.fSaveLayerFlags & kF16ColorType) {
1174 info = info.makeColorType(kRGBA_F16_SkColorType);
1175 }
reed129ed1c2016-02-22 06:42:31 -08001176
Hal Canary704cd322016-11-07 14:13:52 -05001177 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001178 {
Florin Malita4571e492019-07-16 10:25:58 -04001179 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001180 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001181 const bool trackCoverage =
1182 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001183 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001184 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001185 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001186 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1187 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001188 return;
reed61f501f2015-04-29 08:34:00 -07001189 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001190 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001191 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192
Mike Reedb43a3e02017-02-11 10:18:58 -05001193 // only have a "next" if this new layer doesn't affect the clip (rare)
1194 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 fMCRec->fLayer = layer;
1196 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001197
Mike Reedc61abee2017-02-28 17:45:27 -05001198 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001199 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001200 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001201 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001202
Mike Reedc42a1cd2017-02-14 14:25:14 -05001203 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1204
1205 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1206 if (layer->fNext) {
1207 // need to punch a hole in the previous device, so we don't draw there, given that
1208 // the new top-layer will allow drawing to happen "below" it.
1209 SkRegion hole(ir);
1210 do {
1211 layer = layer->fNext;
1212 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1213 } while (layer->fNext);
1214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215}
1216
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001217int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001218 if (0xFF == alpha) {
1219 return this->saveLayer(bounds, nullptr);
1220 } else {
1221 SkPaint tmpPaint;
1222 tmpPaint.setAlpha(alpha);
1223 return this->saveLayer(bounds, &tmpPaint);
1224 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001225}
1226
Mike Reed148b7fd2018-12-18 17:38:18 -05001227void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001228 SkBaseDevice* device = this->getTopDevice();
1229 if (nullptr == device) { // Do we still need this check???
1230 return;
1231 }
1232
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001233 // Map the local bounds into the top device's coordinate space (this is not
1234 // necessarily the full global CTM transform).
1235 SkIRect devBounds;
1236 if (localBounds) {
1237 SkRect tmp;
1238 device->localToDevice().mapRect(&tmp, *localBounds);
1239 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1240 devBounds.setEmpty();
1241 }
1242 } else {
1243 devBounds = device->devClipBounds();
1244 }
1245 if (devBounds.isEmpty()) {
1246 return;
1247 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001248
Michael Ludwigac352122019-08-28 21:03:05 +00001249 // This is getting the special image from the current device, which is then drawn into (both by
1250 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1251 // own device, we need to explicitly copy the back image contents so that its original content
1252 // is available when we splat it back later during restore.
1253 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001254 if (!backImage) {
1255 return;
1256 }
1257
1258 // we really need the save, so we can wack the fMCRec
1259 this->checkForDeferredSave();
1260
1261 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1262
1263 SkPaint paint;
1264 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001265 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001266}
1267
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268void SkCanvas::internalRestore() {
1269 SkASSERT(fMCStack.count() != 0);
1270
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001271 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 DeviceCM* layer = fMCRec->fLayer; // may be null
1273 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001274 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275
Mike Reed148b7fd2018-12-18 17:38:18 -05001276 // move this out before we do the actual restore
1277 auto backImage = std::move(fMCRec->fBackImage);
1278
Mike Reedb18e74d2020-01-16 13:58:22 -05001279 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1280 fCameraStack.pop_back();
1281 }
1282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 // now do the normal restore()
1284 fMCRec->~MCRec(); // balanced in save()
1285 fMCStack.pop_back();
1286 fMCRec = (MCRec*)fMCStack.back();
1287
Mike Reedc42a1cd2017-02-14 14:25:14 -05001288 if (fMCRec) {
1289 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1290 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001291
Mike Reed148b7fd2018-12-18 17:38:18 -05001292 if (backImage) {
1293 SkPaint paint;
1294 paint.setBlendMode(SkBlendMode::kDstOver);
1295 const int x = backImage->fLoc.x();
1296 const int y = backImage->fLoc.y();
1297 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1298 nullptr, SkMatrix::I());
1299 }
1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1302 since if we're being recorded, we don't want to record this (the
1303 recorder will have already recorded the restore).
1304 */
bsalomon49f085d2014-09-05 13:34:00 -07001305 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001306 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001307 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001308 // At this point, 'layer' has been removed from the device stack, so the devices that
1309 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1310 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001311 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001312 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001313 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001314 delete layer;
reedb679ca82015-04-07 04:40:48 -07001315 } else {
1316 // we're at the root
reeda499f902015-05-01 09:34:31 -07001317 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001318 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001319 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001321 }
msarettfbfa2582016-08-12 08:29:08 -07001322
1323 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001324 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001325 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
reede8f30622016-03-23 18:59:25 -07001329sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001330 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001331 props = &fProps;
1332 }
1333 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001334}
1335
reede8f30622016-03-23 18:59:25 -07001336sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001337 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001338 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001339}
1340
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001341SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001342 return this->onImageInfo();
1343}
1344
1345SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001346 SkBaseDevice* dev = this->getDevice();
1347 if (dev) {
1348 return dev->imageInfo();
1349 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001350 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001351 }
1352}
1353
brianosman898235c2016-04-06 07:38:23 -07001354bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001355 return this->onGetProps(props);
1356}
1357
1358bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001359 SkBaseDevice* dev = this->getDevice();
1360 if (dev) {
1361 if (props) {
1362 *props = fProps;
1363 }
1364 return true;
1365 } else {
1366 return false;
1367 }
1368}
1369
reed6ceeebd2016-03-09 14:26:26 -08001370bool SkCanvas::peekPixels(SkPixmap* pmap) {
1371 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001372}
1373
reed884e97c2015-05-26 11:31:54 -07001374bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001376 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377}
1378
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001379void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001380 SkPixmap pmap;
1381 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001382 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001383 }
1384 if (info) {
1385 *info = pmap.info();
1386 }
1387 if (rowBytes) {
1388 *rowBytes = pmap.rowBytes();
1389 }
1390 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001391 // If the caller requested the origin, they presumably are expecting the returned pixels to
1392 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1393 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1394 // instead of an origin, just don't expose the pixels in that case. Note that this means
1395 // that layers with complex coordinate spaces can still report their pixels if the caller
1396 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1397 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1398 *origin = this->getTopDevice()->getOrigin();
1399 } else {
1400 return nullptr;
1401 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001402 }
reed884e97c2015-05-26 11:31:54 -07001403 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001404}
1405
reed884e97c2015-05-26 11:31:54 -07001406bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001407 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001408 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001409}
1410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
Mike Reed8bcd1282019-03-13 16:51:54 -04001413// In our current design/features, we should never have a layer (src) in a different colorspace
1414// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1415// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1416// colorspace.
1417static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1418 SkASSERT(src == dst);
1419}
1420
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001421void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001422 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001424 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 paint = &tmp;
1426 }
reed@google.com4b226022011-01-11 18:32:13 +00001427
Mike Reed38992392019-07-30 10:48:15 -04001428 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001431 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001432 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1433 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001434 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001435 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001436 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1437 // so it should always be possible to use the relative origin. Once drawDevice() and
1438 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1439 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001440 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001441 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1442 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001443 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1444 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001445 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1446 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001447 }
reed@google.com76dd2772012-01-05 21:15:07 +00001448 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001449 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 }
reeda2217ef2016-07-20 06:04:34 -07001452
Mike Reed38992392019-07-30 10:48:15 -04001453 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454}
1455
reed32704672015-12-16 08:27:10 -08001456/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001457
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001458void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001459 if (dx || dy) {
1460 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001461 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001462
reedfe69b502016-09-12 06:31:48 -07001463 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001464 // However, if either is non-finite, we might still complicate the matrix type,
1465 // so we still have to compute this.
1466 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001467
Mike Reedc42a1cd2017-02-14 14:25:14 -05001468 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001469
reedfe69b502016-09-12 06:31:48 -07001470 this->didTranslate(dx,dy);
1471 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001475 if (sx != 1 || sy != 1) {
1476 this->checkForDeferredSave();
1477 fMCRec->fMatrix.preScale(sx, sy);
1478
1479 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1480 // but pre-scaling by a non-finite does change it, so we have to recompute.
1481 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1482
1483 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1484
1485 this->didScale(sx, sy);
1486 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487}
1488
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001489void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001490 SkMatrix m;
1491 m.setRotate(degrees);
1492 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
bungeman7438bfc2016-07-12 15:01:19 -07001495void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1496 SkMatrix m;
1497 m.setRotate(degrees, px, py);
1498 this->concat(m);
1499}
1500
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001501void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001502 SkMatrix m;
1503 m.setSkew(sx, sy);
1504 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505}
1506
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001507void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001508 if (matrix.isIdentity()) {
1509 return;
1510 }
1511
reed2ff1fce2014-12-11 07:07:37 -08001512 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001513 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001514
msarett9637ea92016-08-18 14:03:30 -07001515 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001516
Mike Reed7627fa52017-02-08 10:07:53 -05001517 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001518
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001519 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001520}
1521
Mike Reed46f5c5f2020-02-20 15:42:29 -05001522void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001523 this->checkForDeferredSave();
1524
Mike Reed46f5c5f2020-02-20 15:42:29 -05001525 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001526
1527 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1528
1529 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1530
Mike Reed46f5c5f2020-02-20 15:42:29 -05001531 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001532}
Mike Reed064c7f92020-01-08 17:33:04 -05001533
Mike Reed46f5c5f2020-02-20 15:42:29 -05001534void SkCanvas::concat44(const SkM44& m) {
1535 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001536}
1537
reed8c30a812016-04-20 16:36:51 -07001538void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001539 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001540 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001541
Mike Reedc42a1cd2017-02-14 14:25:14 -05001542 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001543}
1544
1545void SkCanvas::setMatrix(const SkMatrix& matrix) {
1546 this->checkForDeferredSave();
1547 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001548 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549}
1550
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001552 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553}
1554
1555//////////////////////////////////////////////////////////////////////////////
1556
Mike Reedc1f77742016-12-09 09:00:50 -05001557void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001558 if (!rect.isFinite()) {
1559 return;
1560 }
reed2ff1fce2014-12-11 07:07:37 -08001561 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1563 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001564}
1565
Mike Reedc1f77742016-12-09 09:00:50 -05001566void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001567 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001568
Mike Reed7627fa52017-02-08 10:07:53 -05001569 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001570
reedc64eff52015-11-21 12:39:45 -08001571 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001572 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1573 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001574 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001577void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1578 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001579 if (fClipRestrictionRect.isEmpty()) {
1580 // we notify the device, but we *dont* resolve deferred saves (since we're just
1581 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001582 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001583 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001584 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001585 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001586 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001587 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001588 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1589 }
1590}
1591
Mike Reedc1f77742016-12-09 09:00:50 -05001592void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001593 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001595 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1597 } else {
1598 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001599 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001601
Mike Reedc1f77742016-12-09 09:00:50 -05001602void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001604
Brian Salomona3b45d42016-10-03 11:36:16 -04001605 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001606
Mike Reed7627fa52017-02-08 10:07:53 -05001607 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001608
Mike Reed20800c82017-11-15 16:09:04 -05001609 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1610 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001611 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001612}
1613
Mike Reedc1f77742016-12-09 09:00:50 -05001614void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001615 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001617
1618 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1619 SkRect r;
1620 if (path.isRect(&r)) {
1621 this->onClipRect(r, op, edgeStyle);
1622 return;
1623 }
1624 SkRRect rrect;
1625 if (path.isOval(&r)) {
1626 rrect.setOval(r);
1627 this->onClipRRect(rrect, op, edgeStyle);
1628 return;
1629 }
1630 if (path.isRRect(&rrect)) {
1631 this->onClipRRect(rrect, op, edgeStyle);
1632 return;
1633 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 }
robertphillips39f05382015-11-24 09:30:12 -08001635
1636 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637}
1638
Mike Reedc1f77742016-12-09 09:00:50 -05001639void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001640 AutoValidateClip avc(this);
1641
Brian Salomona3b45d42016-10-03 11:36:16 -04001642 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001643
Mike Reed7627fa52017-02-08 10:07:53 -05001644 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645
Brian Salomona3b45d42016-10-03 11:36:16 -04001646 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001647 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001648 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001649 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650}
1651
Mike Reed121c2af2020-03-10 14:02:56 -04001652void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1653 if (sh) {
1654 this->onClipShader(std::move(sh), op);
1655 }
1656}
1657
1658void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1659 AutoValidateClip avc(this);
1660
1661 FOR_EACH_TOP_DEVICE(device->clipShader(sh, op));
1662
1663 // we don't know how to mutate our conservative bounds, so we don't
1664}
1665
Mike Reedc1f77742016-12-09 09:00:50 -05001666void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001667 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001668 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001669}
1670
Mike Reedc1f77742016-12-09 09:00:50 -05001671void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001672 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001673
reed@google.com5c3d1472011-02-22 19:12:23 +00001674 AutoValidateClip avc(this);
1675
Mike Reed20800c82017-11-15 16:09:04 -05001676 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001677 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678}
1679
reed@google.com819c9212011-02-23 18:56:55 +00001680#ifdef SK_DEBUG
1681void SkCanvas::validateClip() const {
1682 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001683 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001684 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001685 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001686 return;
1687 }
reed@google.com819c9212011-02-23 18:56:55 +00001688}
1689#endif
1690
Mike Reeda1361362017-03-07 09:37:29 -05001691bool SkCanvas::androidFramework_isClipAA() const {
1692 bool containsAA = false;
1693
1694 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1695
1696 return containsAA;
1697}
1698
1699class RgnAccumulator {
1700 SkRegion* fRgn;
1701public:
1702 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1703 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1704 SkIPoint origin = device->getOrigin();
1705 if (origin.x() | origin.y()) {
1706 rgn->translate(origin.x(), origin.y());
1707 }
1708 fRgn->op(*rgn, SkRegion::kUnion_Op);
1709 }
1710};
1711
1712void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1713 RgnAccumulator accum(rgn);
1714 SkRegion tmp;
1715
1716 rgn->setEmpty();
1717 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001718}
1719
reed@google.com5c3d1472011-02-22 19:12:23 +00001720///////////////////////////////////////////////////////////////////////////////
1721
reed@google.com754de5f2014-02-24 19:38:20 +00001722bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001723 return fMCRec->fRasterClip.isEmpty();
1724
1725 // TODO: should we only use the conservative answer in a recording canvas?
1726#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001727 SkBaseDevice* dev = this->getTopDevice();
1728 // if no device we return true
1729 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001730#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001731}
1732
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001733bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001734 SkBaseDevice* dev = this->getTopDevice();
1735 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001736 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001737}
1738
msarettfbfa2582016-08-12 08:29:08 -07001739static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1740#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1741 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1742 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1743 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1744 return 0xF != _mm_movemask_ps(mask);
1745#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1746 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1747 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1748 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1749 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1750#else
1751 SkRect devRectAsRect;
1752 SkRect devClipAsRect;
1753 devRect.store(&devRectAsRect.fLeft);
1754 devClip.store(&devClipAsRect.fLeft);
1755 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1756#endif
1757}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001758
msarettfbfa2582016-08-12 08:29:08 -07001759// It's important for this function to not be inlined. Otherwise the compiler will share code
1760// between the fast path and the slow path, resulting in two slow paths.
1761static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1762 const SkMatrix& matrix) {
1763 SkRect deviceRect;
1764 matrix.mapRect(&deviceRect, src);
1765 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1766}
1767
1768bool SkCanvas::quickReject(const SkRect& src) const {
1769#ifdef SK_DEBUG
1770 // Verify that fDeviceClipBounds are set properly.
1771 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001772 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001773 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001774 } else {
msarettfbfa2582016-08-12 08:29:08 -07001775 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776 }
msarettfbfa2582016-08-12 08:29:08 -07001777
msarett9637ea92016-08-18 14:03:30 -07001778 // Verify that fIsScaleTranslate is set properly.
1779 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001780#endif
1781
msarett9637ea92016-08-18 14:03:30 -07001782 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001783 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1784 }
1785
1786 // We inline the implementation of mapScaleTranslate() for the fast path.
1787 float sx = fMCRec->fMatrix.getScaleX();
1788 float sy = fMCRec->fMatrix.getScaleY();
1789 float tx = fMCRec->fMatrix.getTranslateX();
1790 float ty = fMCRec->fMatrix.getTranslateY();
1791 Sk4f scale(sx, sy, sx, sy);
1792 Sk4f trans(tx, ty, tx, ty);
1793
1794 // Apply matrix.
1795 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1796
1797 // Make sure left < right, top < bottom.
1798 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1799 Sk4f min = Sk4f::Min(ltrb, rblt);
1800 Sk4f max = Sk4f::Max(ltrb, rblt);
1801 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1802 // ARM this sequence generates the fastest (a single instruction).
1803 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1804
1805 // Check if the device rect is NaN or outside the clip.
1806 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807}
1808
reed@google.com3b3e8952012-08-16 20:53:31 +00001809bool SkCanvas::quickReject(const SkPath& path) const {
1810 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811}
1812
Mike Klein83c8dd92017-11-28 17:08:45 -05001813SkRect SkCanvas::getLocalClipBounds() const {
1814 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001815 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001816 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 }
1818
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001819 SkMatrix inverse;
1820 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001821 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001822 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001823 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824
Mike Reed42e8c532017-01-23 14:09:13 -05001825 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001826 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001827 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001828
Mike Reedb57b9312018-04-23 12:12:54 -04001829 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001830 inverse.mapRect(&bounds, r);
1831 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832}
1833
Mike Klein83c8dd92017-11-28 17:08:45 -05001834SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001835 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001836}
1837
Mike Reedb18e74d2020-01-16 13:58:22 -05001838///////////////////////////////////////////////////////////////////////
1839
1840SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1841 : fMCRec(owner)
1842 , fCamera(camera)
1843{
1844 // assumes the mcrec has already been concatenated with the camera
1845 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1846 fInvPostCamera.setIdentity();
1847 }
1848}
1849
Mike Reed403c8072020-01-08 10:40:39 -05001850SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001851 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852}
1853
Mike Reed46f5c5f2020-02-20 15:42:29 -05001854SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001855 return fMCRec->fMatrix;
1856}
1857
Mike Reedc43f2a02020-01-16 14:54:34 -05001858SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001859 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001860 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001861 } else {
1862 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001863 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001864 }
1865}
1866
Mike Reedc43f2a02020-01-16 14:54:34 -05001867SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001868 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001869 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001870 } else {
1871 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001872 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001873 }
1874}
1875
Mike Reed46f5c5f2020-02-20 15:42:29 -05001876void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1877 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001878}
1879
1880void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1881 this->experimental_getLocalToWorld().getColMajor(colMajor);
1882}
1883
1884void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1885 this->experimental_getLocalToCamera().getColMajor(colMajor);
1886}
1887
Brian Osman11052242016-10-27 14:47:55 -04001888GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001889 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001890 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001891}
1892
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001893GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001894 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001895 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001896}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001897
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001898void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1899 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001900 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001901 if (outer.isEmpty()) {
1902 return;
1903 }
1904 if (inner.isEmpty()) {
1905 this->drawRRect(outer, paint);
1906 return;
1907 }
1908
1909 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001910 // be able to return ...
1911 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001912 //
1913 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001914 if (!outer.getBounds().contains(inner.getBounds())) {
1915 return;
1916 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001917
1918 this->onDrawDRRect(outer, inner, paint);
1919}
1920
reed41af9662015-01-05 07:49:08 -08001921void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001922 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001923 this->onDrawPaint(paint);
1924}
1925
1926void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001927 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001928 // To avoid redundant logic in our culling code and various backends, we always sort rects
1929 // before passing them along.
1930 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001931}
1932
Mike Reedd5674082019-04-19 15:00:47 -04001933void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1934 TRACE_EVENT0("skia", TRACE_FUNC);
1935 this->onDrawBehind(paint);
1936}
1937
msarettdca352e2016-08-26 06:37:45 -07001938void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001939 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001940 if (region.isEmpty()) {
1941 return;
1942 }
1943
1944 if (region.isRect()) {
1945 return this->drawIRect(region.getBounds(), paint);
1946 }
1947
1948 this->onDrawRegion(region, paint);
1949}
1950
reed41af9662015-01-05 07:49:08 -08001951void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001952 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001953 // To avoid redundant logic in our culling code and various backends, we always sort rects
1954 // before passing them along.
1955 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001956}
1957
1958void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001959 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001960 this->onDrawRRect(rrect, paint);
1961}
1962
1963void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001964 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001965 this->onDrawPoints(mode, count, pts, paint);
1966}
1967
Mike Reede88a1cb2017-03-17 09:50:46 -04001968void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1969 const SkPaint& paint) {
Mike Reed5caf9352020-03-02 14:57:09 -05001970 this->drawVertices(vertices.get(), mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001971}
1972
1973void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001974 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001975 RETURN_ON_NULL(vertices);
Mike Reed5caf9352020-03-02 14:57:09 -05001976 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1977 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
1978 this->onDrawVerticesObject(vertices, mode, paint);
reed41af9662015-01-05 07:49:08 -08001979}
1980
1981void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001982 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001983 this->onDrawPath(path, paint);
1984}
1985
reeda85d4d02015-05-06 12:56:48 -07001986void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001987 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001988 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001989 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001990}
1991
Mike Reedc4e31092018-01-30 11:15:27 -05001992// Returns true if the rect can be "filled" : non-empty and finite
1993static bool fillable(const SkRect& r) {
1994 SkScalar w = r.width();
1995 SkScalar h = r.height();
1996 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1997}
1998
reede47829b2015-08-06 10:02:53 -07001999void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2000 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002001 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002002 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002003 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002004 return;
2005 }
2006 this->onDrawImageRect(image, &src, dst, paint, constraint);
2007}
reed41af9662015-01-05 07:49:08 -08002008
reed84984ef2015-07-17 07:09:43 -07002009void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2010 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002011 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002012 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002013}
2014
Brian Salomonf08002c2018-10-26 16:15:46 -04002015void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002016 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002017 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002018 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002019}
reede47829b2015-08-06 10:02:53 -07002020
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002021namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002022class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002023public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002024 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2025 if (!origPaint) {
2026 return;
2027 }
2028 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2029 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2030 }
2031 if (origPaint->getMaskFilter()) {
2032 fPaint.writable()->setMaskFilter(nullptr);
2033 }
2034 if (origPaint->isAntiAlias()) {
2035 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002036 }
2037 }
2038
2039 const SkPaint* get() const {
2040 return fPaint;
2041 }
2042
2043private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002044 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002045};
2046} // namespace
2047
reed4c21dc52015-06-25 12:32:03 -07002048void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2049 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002050 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002051 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002052 if (dst.isEmpty()) {
2053 return;
2054 }
msarett552bca92016-08-03 06:53:26 -07002055 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002056 LatticePaint latticePaint(paint);
2057 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002058 } else {
reede47829b2015-08-06 10:02:53 -07002059 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002060 }
reed4c21dc52015-06-25 12:32:03 -07002061}
2062
msarett16882062016-08-16 09:31:08 -07002063void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2064 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002065 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002066 RETURN_ON_NULL(image);
2067 if (dst.isEmpty()) {
2068 return;
2069 }
msarett71df2d72016-09-30 12:41:42 -07002070
2071 SkIRect bounds;
2072 Lattice latticePlusBounds = lattice;
2073 if (!latticePlusBounds.fBounds) {
2074 bounds = SkIRect::MakeWH(image->width(), image->height());
2075 latticePlusBounds.fBounds = &bounds;
2076 }
2077
2078 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002079 LatticePaint latticePaint(paint);
2080 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002081 } else {
2082 this->drawImageRect(image, dst, paint);
2083 }
2084}
2085
reed41af9662015-01-05 07:49:08 -08002086void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002087 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002088 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002089 return;
2090 }
reed41af9662015-01-05 07:49:08 -08002091 this->onDrawBitmap(bitmap, dx, dy, paint);
2092}
2093
reede47829b2015-08-06 10:02:53 -07002094void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002095 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002096 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002097 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002098 return;
2099 }
reede47829b2015-08-06 10:02:53 -07002100 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002101}
2102
reed84984ef2015-07-17 07:09:43 -07002103void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2104 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002105 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002106}
2107
reede47829b2015-08-06 10:02:53 -07002108void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2109 SrcRectConstraint constraint) {
2110 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2111 constraint);
2112}
reede47829b2015-08-06 10:02:53 -07002113
reed41af9662015-01-05 07:49:08 -08002114void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2115 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002116 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002117 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002118 return;
2119 }
msarett552bca92016-08-03 06:53:26 -07002120 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002121 LatticePaint latticePaint(paint);
2122 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002123 } else {
reeda5517e22015-07-14 10:54:12 -07002124 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002125 }
reed41af9662015-01-05 07:49:08 -08002126}
2127
msarettc573a402016-08-02 08:05:56 -07002128void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2129 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002130 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002131 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002132 return;
2133 }
msarett71df2d72016-09-30 12:41:42 -07002134
2135 SkIRect bounds;
2136 Lattice latticePlusBounds = lattice;
2137 if (!latticePlusBounds.fBounds) {
2138 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2139 latticePlusBounds.fBounds = &bounds;
2140 }
2141
2142 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002143 LatticePaint latticePaint(paint);
2144 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002145 } else {
msarett16882062016-08-16 09:31:08 -07002146 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002147 }
msarettc573a402016-08-02 08:05:56 -07002148}
2149
reed71c3c762015-06-24 10:29:17 -07002150void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002151 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002152 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002153 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002154 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002155 if (count <= 0) {
2156 return;
2157 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002158 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002159 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002160 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002161}
2162
reedf70b5312016-03-04 16:36:20 -08002163void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002164 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002165 if (key) {
2166 this->onDrawAnnotation(rect, key, value);
2167 }
2168}
2169
reede47829b2015-08-06 10:02:53 -07002170void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2171 const SkPaint* paint, SrcRectConstraint constraint) {
2172 if (src) {
2173 this->drawImageRect(image, *src, dst, paint, constraint);
2174 } else {
2175 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2176 dst, paint, constraint);
2177 }
2178}
2179void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2180 const SkPaint* paint, SrcRectConstraint constraint) {
2181 if (src) {
2182 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2183 } else {
2184 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2185 dst, paint, constraint);
2186 }
2187}
2188
Mike Reed4204da22017-05-17 08:53:36 -04002189void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002190 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002191 this->onDrawShadowRec(path, rec);
2192}
2193
2194void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2195 SkPaint paint;
2196 const SkRect& pathBounds = path.getBounds();
2197
Mike Reed38992392019-07-30 10:48:15 -04002198 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002199 while (iter.next()) {
2200 iter.fDevice->drawShadow(path, rec);
2201 }
Mike Reed38992392019-07-30 10:48:15 -04002202 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002203}
2204
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002205void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002206 QuadAAFlags aaFlags, const SkColor4f& color,
2207 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002208 TRACE_EVENT0("skia", TRACE_FUNC);
2209 // Make sure the rect is sorted before passing it along
2210 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2211}
2212
2213void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2214 const SkPoint dstClips[],
2215 const SkMatrix preViewMatrices[],
2216 const SkPaint* paint,
2217 SrcRectConstraint constraint) {
2218 TRACE_EVENT0("skia", TRACE_FUNC);
2219 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2220}
2221
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222//////////////////////////////////////////////////////////////////////////////
2223// These are the virtual drawing methods
2224//////////////////////////////////////////////////////////////////////////////
2225
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002226void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002227 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002228 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2229 }
2230}
2231
reed41af9662015-01-05 07:49:08 -08002232void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002233 this->internalDrawPaint(paint);
2234}
2235
2236void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002237 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238
2239 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002240 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002241 }
2242
Mike Reed38992392019-07-30 10:48:15 -04002243 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244}
2245
reed41af9662015-01-05 07:49:08 -08002246void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2247 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 if ((long)count <= 0) {
2249 return;
2250 }
2251
Mike Reed822128b2017-02-28 16:41:03 -05002252 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002253 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002254 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002255 // special-case 2 points (common for drawing a single line)
2256 if (2 == count) {
2257 r.set(pts[0], pts[1]);
2258 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002259 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002260 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002261 if (!r.isFinite()) {
2262 return;
2263 }
Mike Reed822128b2017-02-28 16:41:03 -05002264 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002265 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2266 return;
2267 }
2268 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002269 }
reed@google.coma584aed2012-05-16 14:06:02 +00002270
halcanary96fcdcc2015-08-27 07:41:13 -07002271 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272
Mike Reed38992392019-07-30 10:48:15 -04002273 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002274
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002276 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 }
reed@google.com4b226022011-01-11 18:32:13 +00002278
Mike Reed38992392019-07-30 10:48:15 -04002279 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280}
2281
reed4a167172016-08-18 17:15:25 -07002282static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002283 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002284}
2285
reed41af9662015-01-05 07:49:08 -08002286void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002287 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002289 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002290 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002291 return;
2292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293 }
reed@google.com4b226022011-01-11 18:32:13 +00002294
reed4a167172016-08-18 17:15:25 -07002295 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002296 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297
reed4a167172016-08-18 17:15:25 -07002298 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002299 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002300 }
2301
Mike Reed38992392019-07-30 10:48:15 -04002302 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002303 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002304 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002305 SkDrawIter iter(this);
2306 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002307 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002308 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310}
2311
msarett44df6512016-08-25 13:54:30 -07002312void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002313 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002314 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002315 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002316 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2317 return;
2318 }
msarett44df6512016-08-25 13:54:30 -07002319 }
2320
Mike Reed38992392019-07-30 10:48:15 -04002321 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002322
2323 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002324 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002325 }
2326
Mike Reed38992392019-07-30 10:48:15 -04002327 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002328}
2329
Mike Reedd5674082019-04-19 15:00:47 -04002330void SkCanvas::onDrawBehind(const SkPaint& paint) {
2331 SkIRect bounds;
2332 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2333 for (;;) {
2334 const MCRec* rec = (const MCRec*)iter.prev();
2335 if (!rec) {
2336 return; // no backimages, so nothing to draw
2337 }
2338 if (rec->fBackImage) {
2339 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2340 rec->fBackImage->fImage->width(),
2341 rec->fBackImage->fImage->height());
2342 break;
2343 }
2344 }
2345
Mike Reed38992392019-07-30 10:48:15 -04002346 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002347
2348 while (iter.next()) {
2349 SkBaseDevice* dev = iter.fDevice;
2350
Mike Reedd5674082019-04-19 15:00:47 -04002351 dev->save();
2352 // We use clipRegion because it is already defined to operate in dev-space
2353 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2354 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002355 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002356 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002357 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002358 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002359 }
2360
Mike Reed38992392019-07-30 10:48:15 -04002361 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002362}
2363
reed41af9662015-01-05 07:49:08 -08002364void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002365 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002366 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002367 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002368 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002369 return;
2370 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002371 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002372
Mike Reed38992392019-07-30 10:48:15 -04002373 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002374
2375 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002376 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002377 }
2378
Mike Reed38992392019-07-30 10:48:15 -04002379 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002380}
2381
bsalomonac3aa242016-08-19 11:25:19 -07002382void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2383 SkScalar sweepAngle, bool useCenter,
2384 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002385 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002386 if (paint.canComputeFastBounds()) {
2387 SkRect storage;
2388 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002389 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002390 return;
2391 }
bsalomonac3aa242016-08-19 11:25:19 -07002392 }
2393
Mike Reed38992392019-07-30 10:48:15 -04002394 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002395
2396 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002397 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002398 }
2399
Mike Reed38992392019-07-30 10:48:15 -04002400 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002401}
2402
reed41af9662015-01-05 07:49:08 -08002403void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002404 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002405 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002406 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2407 return;
2408 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002409 }
2410
2411 if (rrect.isRect()) {
2412 // call the non-virtual version
2413 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002414 return;
2415 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002416 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002417 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2418 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002419 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002420
Mike Reed38992392019-07-30 10:48:15 -04002421 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002422
2423 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002424 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002425 }
2426
Mike Reed38992392019-07-30 10:48:15 -04002427 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002428}
2429
Mike Reed822128b2017-02-28 16:41:03 -05002430void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002431 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002432 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002433 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2434 return;
2435 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002436 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002437
Mike Reed38992392019-07-30 10:48:15 -04002438 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002439
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002440 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002441 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002442 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002443
Mike Reed38992392019-07-30 10:48:15 -04002444 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002445}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002446
reed41af9662015-01-05 07:49:08 -08002447void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002448 if (!path.isFinite()) {
2449 return;
2450 }
2451
Mike Reed822128b2017-02-28 16:41:03 -05002452 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002453 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002454 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002455 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2456 return;
2457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002458 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002459
Mike Reed822128b2017-02-28 16:41:03 -05002460 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002461 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002462 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002463 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002464 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002465 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466
Mike Reed38992392019-07-30 10:48:15 -04002467 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468
2469 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002470 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471 }
2472
Mike Reed38992392019-07-30 10:48:15 -04002473 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474}
2475
reed262a71b2015-12-05 13:07:27 -08002476bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002477 if (!paint.getImageFilter()) {
2478 return false;
2479 }
2480
2481 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002482 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002483 return false;
2484 }
2485
Michael Ludwig1e58c1a2020-03-02 16:27:03 -05002486 // The other paint effects need to be applied before the image filter, but the sprite draw
2487 // applies the filter explicitly first.
2488 if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
2489 return false;
2490 }
reed262a71b2015-12-05 13:07:27 -08002491 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2492 // Once we can filter and the filter will return a result larger than itself, we should be
2493 // able to remove this constraint.
2494 // skbug.com/4526
2495 //
2496 SkPoint pt;
2497 ctm.mapXY(x, y, &pt);
2498 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2499 return ir.contains(fMCRec->fRasterClip.getBounds());
2500}
2501
Mike Reedf441cfc2018-04-11 14:50:16 -04002502// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2503// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2504// null.
2505static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2506 if (paintParam) {
2507 *real = *paintParam;
2508 real->setStyle(SkPaint::kFill_Style);
2509 real->setPathEffect(nullptr);
2510 paintParam = real;
2511 }
2512 return paintParam;
2513}
2514
reeda85d4d02015-05-06 12:56:48 -07002515void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002516 SkPaint realPaint;
2517 paint = init_image_paint(&realPaint, paint);
2518
reeda85d4d02015-05-06 12:56:48 -07002519 SkRect bounds = SkRect::MakeXYWH(x, y,
2520 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002521 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002522 SkRect tmp = bounds;
2523 if (paint) {
2524 paint->computeFastBounds(tmp, &tmp);
2525 }
2526 if (this->quickReject(tmp)) {
2527 return;
2528 }
reeda85d4d02015-05-06 12:56:48 -07002529 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002530 // At this point we need a real paint object. If the caller passed null, then we should
2531 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2532 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2533 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002534
reeda2217ef2016-07-20 06:04:34 -07002535 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002536 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2537 *paint);
2538 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002539 special = this->getDevice()->makeSpecial(image);
2540 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002541 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002542 }
2543 }
2544
Mike Reed38992392019-07-30 10:48:15 -04002545 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002546
reeda85d4d02015-05-06 12:56:48 -07002547 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002548 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002549 if (special) {
2550 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002551 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002552 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002553 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002554 SkScalarRoundToInt(pt.fY), pnt,
2555 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002556 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002557 iter.fDevice->drawImageRect(
2558 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2559 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002560 }
reeda85d4d02015-05-06 12:56:48 -07002561 }
halcanary9d524f22016-03-29 09:03:52 -07002562
Mike Reed38992392019-07-30 10:48:15 -04002563 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002564}
2565
reed41af9662015-01-05 07:49:08 -08002566void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002567 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002568 SkPaint realPaint;
2569 paint = init_image_paint(&realPaint, paint);
2570
halcanary96fcdcc2015-08-27 07:41:13 -07002571 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002572 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002573 if (paint) {
2574 paint->computeFastBounds(dst, &storage);
2575 }
2576 if (this->quickReject(storage)) {
2577 return;
2578 }
reeda85d4d02015-05-06 12:56:48 -07002579 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002580 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002581
Mike Reed38992392019-07-30 10:48:15 -04002582 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002583
reeda85d4d02015-05-06 12:56:48 -07002584 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002585 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002586 }
halcanary9d524f22016-03-29 09:03:52 -07002587
Mike Reed38992392019-07-30 10:48:15 -04002588 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002589}
2590
reed41af9662015-01-05 07:49:08 -08002591void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002592 SkDEBUGCODE(bitmap.validate();)
2593
reed33366972015-10-08 09:22:02 -07002594 if (bitmap.drawsNothing()) {
2595 return;
2596 }
2597
Mike Reedf441cfc2018-04-11 14:50:16 -04002598 SkPaint realPaint;
2599 init_image_paint(&realPaint, paint);
2600 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002601
Mike Reed822128b2017-02-28 16:41:03 -05002602 SkRect bounds;
2603 bitmap.getBounds(&bounds);
2604 bounds.offset(x, y);
2605 bool canFastBounds = paint->canComputeFastBounds();
2606 if (canFastBounds) {
2607 SkRect storage;
2608 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002609 return;
2610 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002611 }
reed@google.com4b226022011-01-11 18:32:13 +00002612
reeda2217ef2016-07-20 06:04:34 -07002613 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002614 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2615 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002616 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002617 special = this->getDevice()->makeSpecial(bitmap);
2618 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002619 drawAsSprite = false;
2620 }
2621 }
2622
Mike Reed38992392019-07-30 10:48:15 -04002623 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002624
2625 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002626 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002627 if (special) {
reed262a71b2015-12-05 13:07:27 -08002628 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002629 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002630 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002631 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002632 SkScalarRoundToInt(pt.fY), pnt,
2633 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002634 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002635 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2636 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2637 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002638 }
reed33366972015-10-08 09:22:02 -07002639 }
msarettfbfa2582016-08-12 08:29:08 -07002640
Mike Reed38992392019-07-30 10:48:15 -04002641 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002642}
2643
reed@google.com9987ec32011-09-07 11:57:52 +00002644// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002645void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002646 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002647 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002648 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002649 return;
2650 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002651
halcanary96fcdcc2015-08-27 07:41:13 -07002652 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002653 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002654 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2655 return;
2656 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002657 }
reed@google.com3d608122011-11-21 15:16:16 +00002658
reed@google.com33535f32012-09-25 15:37:50 +00002659 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002660 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002661 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002662 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002663
Mike Reed38992392019-07-30 10:48:15 -04002664 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002665
reed@google.com33535f32012-09-25 15:37:50 +00002666 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002667 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002668 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002669
Mike Reed38992392019-07-30 10:48:15 -04002670 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002671}
2672
reed41af9662015-01-05 07:49:08 -08002673void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002674 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002675 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002676 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002677}
2678
reed4c21dc52015-06-25 12:32:03 -07002679void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2680 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002681 SkPaint realPaint;
2682 paint = init_image_paint(&realPaint, paint);
2683
halcanary96fcdcc2015-08-27 07:41:13 -07002684 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002685 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002686 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2687 return;
2688 }
reed@google.com3d608122011-11-21 15:16:16 +00002689 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002690 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002691
Mike Reed38992392019-07-30 10:48:15 -04002692 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002693
reed4c21dc52015-06-25 12:32:03 -07002694 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002695 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002696 }
halcanary9d524f22016-03-29 09:03:52 -07002697
Mike Reed38992392019-07-30 10:48:15 -04002698 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002699}
2700
reed41af9662015-01-05 07:49:08 -08002701void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2702 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002703 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002704 SkPaint realPaint;
2705 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002706
halcanary96fcdcc2015-08-27 07:41:13 -07002707 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002708 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002709 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2710 return;
2711 }
reed4c21dc52015-06-25 12:32:03 -07002712 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002713 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002714
Mike Reed38992392019-07-30 10:48:15 -04002715 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002716
reed4c21dc52015-06-25 12:32:03 -07002717 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002718 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002719 }
halcanary9d524f22016-03-29 09:03:52 -07002720
Mike Reed38992392019-07-30 10:48:15 -04002721 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002722}
2723
msarett16882062016-08-16 09:31:08 -07002724void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2725 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002726 SkPaint realPaint;
2727 paint = init_image_paint(&realPaint, paint);
2728
msarett16882062016-08-16 09:31:08 -07002729 if (nullptr == paint || paint->canComputeFastBounds()) {
2730 SkRect storage;
2731 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2732 return;
2733 }
2734 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002735 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002736
Mike Reed38992392019-07-30 10:48:15 -04002737 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002738
2739 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002740 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002741 }
2742
Mike Reed38992392019-07-30 10:48:15 -04002743 DRAW_END
msarett16882062016-08-16 09:31:08 -07002744}
2745
2746void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2747 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002748 SkPaint realPaint;
2749 paint = init_image_paint(&realPaint, paint);
2750
msarett16882062016-08-16 09:31:08 -07002751 if (nullptr == paint || paint->canComputeFastBounds()) {
2752 SkRect storage;
2753 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2754 return;
2755 }
2756 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002757 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002758
Mike Reed38992392019-07-30 10:48:15 -04002759 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002760
2761 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002762 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002763 }
2764
Mike Reed38992392019-07-30 10:48:15 -04002765 DRAW_END
msarett16882062016-08-16 09:31:08 -07002766}
2767
fmalita00d5c2c2014-08-21 08:53:26 -07002768void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2769 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002770 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002771 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002772 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002773 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002774 SkRect tmp;
2775 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2776 return;
2777 }
2778 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002779 }
2780
fmalita024f9962015-03-03 19:08:17 -08002781 // We cannot filter in the looper as we normally do, because the paint is
2782 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002783 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002784
fmalitaaa1b9122014-08-28 14:32:24 -07002785 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002786 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002787 }
2788
Mike Reed38992392019-07-30 10:48:15 -04002789 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002790}
2791
Mike Reed358fcad2018-11-23 15:27:51 -05002792// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002793void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002794 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2795 TRACE_EVENT0("skia", TRACE_FUNC);
2796 if (byteLength) {
2797 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002798 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002799 }
2800}
Mike Reed4f81bb72019-01-23 09:23:00 -05002801
fmalita00d5c2c2014-08-21 08:53:26 -07002802void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2803 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002804 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002805 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002806 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002807 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002808}
reed@google.come0d9ce82014-04-23 04:00:17 +00002809
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002810#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002811void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002812 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002813 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002814
2815 while (iter.next()) {
2816 // In the common case of one iteration we could std::move vertices here.
Mike Reed5caf9352020-03-02 14:57:09 -05002817 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002818 }
2819
Mike Reed38992392019-07-30 10:48:15 -04002820 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002821}
Mike Reeda2cf8ae2020-03-02 17:08:01 -05002822#else
2823void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2824 const SkPaint& paint) {
2825 DRAW_BEGIN(paint, nullptr)
2826
2827 while (iter.next()) {
2828 // In the common case of one iteration we could std::move vertices here.
2829 iter.fDevice->drawVertices(vertices, bmode, draw.paint());
2830 }
2831
2832 DRAW_END
2833}
2834#endif
Brian Salomon199fb872017-02-06 09:41:10 -05002835
dandovb3c9d1c2014-08-12 08:34:29 -07002836void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002837 const SkPoint texCoords[4], SkBlendMode bmode,
2838 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002839 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002840 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002841 return;
2842 }
mtklein6cfa73a2014-08-13 13:33:49 -07002843
Mike Reedfaba3712016-11-03 14:45:31 -04002844 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002845}
2846
2847void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002848 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002849 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002850 // Since a patch is always within the convex hull of the control points, we discard it when its
2851 // bounding rectangle is completely outside the current clip.
2852 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002853 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002854 if (this->quickReject(bounds)) {
2855 return;
2856 }
mtklein6cfa73a2014-08-13 13:33:49 -07002857
Mike Reed38992392019-07-30 10:48:15 -04002858 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002859
dandovecfff212014-08-04 10:02:00 -07002860 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002861 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002862 }
mtklein6cfa73a2014-08-13 13:33:49 -07002863
Mike Reed38992392019-07-30 10:48:15 -04002864 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002865}
2866
reeda8db7282015-07-07 10:22:31 -07002867void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002868#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002869 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002870#endif
reede3b38ce2016-01-08 09:18:44 -08002871 RETURN_ON_NULL(dr);
2872 if (x || y) {
2873 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2874 this->onDrawDrawable(dr, &matrix);
2875 } else {
2876 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002877 }
2878}
2879
reeda8db7282015-07-07 10:22:31 -07002880void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002881#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002882 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002883#endif
reede3b38ce2016-01-08 09:18:44 -08002884 RETURN_ON_NULL(dr);
2885 if (matrix && matrix->isIdentity()) {
2886 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002887 }
reede3b38ce2016-01-08 09:18:44 -08002888 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002889}
2890
2891void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002892 // drawable bounds are no longer reliable (e.g. android displaylist)
2893 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002894 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002895}
2896
reed71c3c762015-06-24 10:29:17 -07002897void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002898 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002899 const SkRect* cull, const SkPaint* paint) {
2900 if (cull && this->quickReject(*cull)) {
2901 return;
2902 }
2903
2904 SkPaint pnt;
2905 if (paint) {
2906 pnt = *paint;
2907 }
halcanary9d524f22016-03-29 09:03:52 -07002908
Mike Reed38992392019-07-30 10:48:15 -04002909 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002910 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002911 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002912 }
Mike Reed38992392019-07-30 10:48:15 -04002913 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002914}
2915
reedf70b5312016-03-04 16:36:20 -08002916void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2917 SkASSERT(key);
2918
2919 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002920 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002921 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002922 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002923 }
Mike Reed38992392019-07-30 10:48:15 -04002924 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002925}
2926
Michael Ludwiga595f862019-08-27 15:25:49 -04002927void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2928 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002929 SkASSERT(r.isSorted());
2930
2931 // If this used a paint, it would be a filled color with blend mode, which does not
2932 // need to use an autodraw loop, so use SkDrawIter directly.
2933 if (this->quickReject(r)) {
2934 return;
2935 }
2936
Michael Ludwiga4b44882019-08-28 14:34:58 -04002937 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002938 SkDrawIter iter(this);
2939 while(iter.next()) {
2940 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2941 }
2942}
2943
2944void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2945 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2946 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002947 if (count <= 0) {
2948 // Nothing to draw
2949 return;
2950 }
2951
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002952 SkPaint realPaint;
2953 init_image_paint(&realPaint, paint);
2954
Michael Ludwiga4b44882019-08-28 14:34:58 -04002955 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2956 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2957 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2958 // or we need it for the autolooper (since it greatly improves image filter perf).
2959 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2960 bool setBoundsValid = count == 1 || needsAutoLooper;
2961 SkRect setBounds = imageSet[0].fDstRect;
2962 if (imageSet[0].fMatrixIndex >= 0) {
2963 // Account for the per-entry transform that is applied prior to the CTM when drawing
2964 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002965 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002966 if (needsAutoLooper) {
2967 for (int i = 1; i < count; ++i) {
2968 SkRect entryBounds = imageSet[i].fDstRect;
2969 if (imageSet[i].fMatrixIndex >= 0) {
2970 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2971 }
2972 setBounds.joinPossiblyEmptyRect(entryBounds);
2973 }
2974 }
2975
2976 // If we happen to have the draw bounds, though, might as well check quickReject().
2977 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2978 SkRect tmp;
2979 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2980 return;
2981 }
2982 }
2983
2984 if (needsAutoLooper) {
2985 SkASSERT(setBoundsValid);
2986 DRAW_BEGIN(realPaint, &setBounds)
2987 while (iter.next()) {
2988 iter.fDevice->drawEdgeAAImageSet(
2989 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2990 }
2991 DRAW_END
2992 } else {
2993 this->predrawNotify();
2994 SkDrawIter iter(this);
2995 while(iter.next()) {
2996 iter.fDevice->drawEdgeAAImageSet(
2997 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2998 }
2999 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003000}
3001
reed@android.com8a1c16f2008-12-17 15:59:43 +00003002//////////////////////////////////////////////////////////////////////////////
3003// These methods are NOT virtual, and therefore must call back into virtual
3004// methods, rather than actually drawing themselves.
3005//////////////////////////////////////////////////////////////////////////////
3006
reed374772b2016-10-05 17:33:02 -07003007void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003008 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07003010 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003011 this->drawPaint(paint);
3012}
3013
3014void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05003015 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00003016 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3017}
3018
Mike Reed3661bc92017-02-22 13:21:42 -05003019void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003020 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021 pts[0].set(x0, y0);
3022 pts[1].set(x1, y1);
3023 this->drawPoints(kLines_PointMode, 2, pts, paint);
3024}
3025
Mike Reed3661bc92017-02-22 13:21:42 -05003026void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003027 if (radius < 0) {
3028 radius = 0;
3029 }
3030
3031 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04003032 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003033 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003034}
3035
3036void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3037 const SkPaint& paint) {
3038 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003039 SkRRect rrect;
3040 rrect.setRectXY(r, rx, ry);
3041 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003042 } else {
3043 this->drawRect(r, paint);
3044 }
3045}
3046
reed@android.com8a1c16f2008-12-17 15:59:43 +00003047void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3048 SkScalar sweepAngle, bool useCenter,
3049 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04003050 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07003051 if (oval.isEmpty() || !sweepAngle) {
3052 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003053 }
bsalomon21af9ca2016-08-25 12:29:23 -07003054 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003055}
3056
reed@android.comf76bacf2009-05-13 14:00:33 +00003057///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003058#ifdef SK_DISABLE_SKPICTURE
3059void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07003060
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003061
3062void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3063 const SkPaint* paint) {}
3064#else
Mike Klein88d90712018-01-27 17:30:04 +00003065/**
3066 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3067 * against the playback cost of recursing into the subpicture to get at its actual ops.
3068 *
3069 * For now we pick a conservatively small value, though measurement (and other heuristics like
3070 * the type of ops contained) may justify changing this value.
3071 */
3072#define kMaxPictureOpsToUnrollInsteadOfRef 1
3073
reedd5fa1a42014-08-09 11:08:05 -07003074void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04003075 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08003076 RETURN_ON_NULL(picture);
3077
reede3b38ce2016-01-08 09:18:44 -08003078 if (matrix && matrix->isIdentity()) {
3079 matrix = nullptr;
3080 }
Mike Klein88d90712018-01-27 17:30:04 +00003081 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3082 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3083 picture->playback(this);
3084 } else {
3085 this->onDrawPicture(picture, matrix, paint);
3086 }
reedd5fa1a42014-08-09 11:08:05 -07003087}
robertphillips9b14f262014-06-04 05:40:44 -07003088
reedd5fa1a42014-08-09 11:08:05 -07003089void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3090 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003091 if (!paint || paint->canComputeFastBounds()) {
3092 SkRect bounds = picture->cullRect();
3093 if (paint) {
3094 paint->computeFastBounds(bounds, &bounds);
3095 }
3096 if (matrix) {
3097 matrix->mapRect(&bounds);
3098 }
3099 if (this->quickReject(bounds)) {
3100 return;
3101 }
3102 }
3103
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003104 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003105 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003106}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003107#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003108
reed@android.com8a1c16f2008-12-17 15:59:43 +00003109///////////////////////////////////////////////////////////////////////////////
3110///////////////////////////////////////////////////////////////////////////////
3111
reed3aafe112016-08-18 12:45:34 -07003112SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003113 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003114
3115 SkASSERT(canvas);
3116
reed3aafe112016-08-18 12:45:34 -07003117 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003118 fDone = !fImpl->next();
3119}
3120
3121SkCanvas::LayerIter::~LayerIter() {
3122 fImpl->~SkDrawIter();
3123}
3124
3125void SkCanvas::LayerIter::next() {
3126 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003127 if (!fDone) {
3128 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
3129 // filters, so its devices will always be able to report the origin exactly.
3130 fDeviceOrigin = fImpl->fDevice->getOrigin();
3131 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003132}
3133
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003134SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003135 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003136}
3137
3138const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003139 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003140}
3141
3142const SkPaint& SkCanvas::LayerIter::paint() const {
3143 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003144 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003145 paint = &fDefaultPaint;
3146 }
3147 return *paint;
3148}
3149
Mike Reedca37f322018-03-08 13:22:16 -05003150SkIRect SkCanvas::LayerIter::clipBounds() const {
3151 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003152}
3153
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003154int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
3155int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003156
3157///////////////////////////////////////////////////////////////////////////////
3158
Brian Osmane8a98632019-04-10 10:26:10 -04003159SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3160SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3161SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3162SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3163
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003164SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3165 const SkRect& dstRect, int matrixIndex, float alpha,
3166 unsigned aaFlags, bool hasClip)
3167 : fImage(std::move(image))
3168 , fSrcRect(srcRect)
3169 , fDstRect(dstRect)
3170 , fMatrixIndex(matrixIndex)
3171 , fAlpha(alpha)
3172 , fAAFlags(aaFlags)
3173 , fHasClip(hasClip) {}
3174
3175SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3176 const SkRect& dstRect, float alpha, unsigned aaFlags)
3177 : fImage(std::move(image))
3178 , fSrcRect(srcRect)
3179 , fDstRect(dstRect)
3180 , fAlpha(alpha)
3181 , fAAFlags(aaFlags) {}
3182
3183///////////////////////////////////////////////////////////////////////////////
3184
Mike Reed5df49342016-11-12 08:06:55 -06003185std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003186 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003187 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003188 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003189 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003190
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003191 SkBitmap bitmap;
3192 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003193 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003194 }
Mike Reed12f77342017-11-08 11:19:52 -05003195
3196 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003197 std::make_unique<SkCanvas>(bitmap, *props) :
3198 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003199}
reedd5fa1a42014-08-09 11:08:05 -07003200
3201///////////////////////////////////////////////////////////////////////////////
3202
Florin Malitaee424ac2016-12-01 12:47:59 -05003203SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003204 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003205
Florin Malita439ace92016-12-02 12:05:41 -05003206SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003207 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003208
Herb Derbyefe39bc2018-05-01 17:06:20 -04003209SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003210 : INHERITED(device) {}
3211
Florin Malitaee424ac2016-12-01 12:47:59 -05003212SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3213 (void)this->INHERITED::getSaveLayerStrategy(rec);
3214 return kNoLayer_SaveLayerStrategy;
3215}
3216
Mike Reed148b7fd2018-12-18 17:38:18 -05003217bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3218 return false;
3219}
3220
Florin Malitaee424ac2016-12-01 12:47:59 -05003221///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003222
reed73603f32016-09-20 08:42:38 -07003223static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3224static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3225static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3226static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3227static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3228static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003229
3230///////////////////////////////////////////////////////////////////////////////////////////////////
3231
3232SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3233 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003234 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003235 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003236 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003237 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003238 clip.setEmpty();
3239 }
3240
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003241 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003242 return handle;
3243 }
3244 return nullptr;
3245}
3246
3247static bool install(SkBitmap* bm, const SkImageInfo& info,
3248 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003249 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003250}
3251
3252SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3253 SkBitmap* bm) {
3254 SkRasterHandleAllocator::Rec rec;
3255 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3256 return nullptr;
3257 }
3258 return rec.fHandle;
3259}
3260
3261std::unique_ptr<SkCanvas>
3262SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3263 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003264 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003265 return nullptr;
3266 }
3267
3268 SkBitmap bm;
3269 Handle hndl;
3270
3271 if (rec) {
3272 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3273 } else {
3274 hndl = alloc->allocBitmap(info, &bm);
3275 }
3276 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3277}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003278
3279///////////////////////////////////////////////////////////////////////////////////////////////////