blob: cdd170a794518257fe7e1f65a203be7266468c8c [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/private/SkNx.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040023#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050025#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050035#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050036#include "src/core/SkMatrixUtils.h"
37#include "src/core/SkPaintPriv.h"
38#include "src/core/SkRasterClip.h"
39#include "src/core/SkSpecialImage.h"
40#include "src/core/SkStrikeCache.h"
41#include "src/core/SkTLazy.h"
42#include "src/core/SkTextFormatParams.h"
43#include "src/core/SkTraceEvent.h"
44#include "src/image/SkImage_Base.h"
45#include "src/image/SkSurface_Base.h"
46#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040047
bungemand3ebb482015-08-05 13:57:49 -070048#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000050#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050051#include "include/gpu/GrContext.h"
52#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000053#endif
54
reede3b38ce2016-01-08 09:18:44 -080055#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050056#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080057
Mike Klein1bb7e232019-12-10 08:58:52 -060058// This is a test: static_assert with no message is a c++17 feature,
59// and std::max() is constexpr only since the c++14 stdlib.
60static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000061
Mike Reed139e5e02017-03-08 11:29:33 -050062///////////////////////////////////////////////////////////////////////////////////////////////////
63
reedc83a2972015-07-16 07:40:45 -070064/*
65 * Return true if the drawing this rect would hit every pixels in the canvas.
66 *
67 * Returns false if
68 * - rect does not contain the canvas' bounds
69 * - paint is not fill
70 * - paint would blur or otherwise change the coverage of the rect
71 */
72bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
73 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070074 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
75 (int)kNone_ShaderOverrideOpacity,
76 "need_matching_enums0");
77 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
78 (int)kOpaque_ShaderOverrideOpacity,
79 "need_matching_enums1");
80 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
81 (int)kNotOpaque_ShaderOverrideOpacity,
82 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070083
84 const SkISize size = this->getBaseLayerSize();
85 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050086
87 // if we're clipped at all, we can't overwrite the entire surface
88 {
89 SkBaseDevice* base = this->getDevice();
90 SkBaseDevice* top = this->getTopDevice();
91 if (base != top) {
92 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
93 }
94 if (!base->clipIsWideOpen()) {
95 return false;
96 }
reedc83a2972015-07-16 07:40:45 -070097 }
98
99 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700100 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700101 return false; // conservative
102 }
halcanaryc5769b22016-08-10 07:13:21 -0700103
104 SkRect devRect;
105 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
106 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700107 return false;
108 }
109 }
110
111 if (paint) {
112 SkPaint::Style paintStyle = paint->getStyle();
113 if (!(paintStyle == SkPaint::kFill_Style ||
114 paintStyle == SkPaint::kStrokeAndFill_Style)) {
115 return false;
116 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400117 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700118 return false; // conservative
119 }
120 }
121 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
122}
123
124///////////////////////////////////////////////////////////////////////////////////////////////////
125
reed@google.comda17f752012-08-16 18:27:05 +0000126// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127//#define SK_TRACE_SAVERESTORE
128
129#ifdef SK_TRACE_SAVERESTORE
130 static int gLayerCounter;
131 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
132 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
133
134 static int gRecCounter;
135 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
136 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
137
138 static int gCanvasCounter;
139 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
140 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
141#else
142 #define inc_layer()
143 #define dec_layer()
144 #define inc_rec()
145 #define dec_rec()
146 #define inc_canvas()
147 #define dec_canvas()
148#endif
149
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000150typedef SkTLazy<SkPaint> SkLazyPaint;
151
reedc83a2972015-07-16 07:40:45 -0700152void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000153 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700154 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
155 ? SkSurface::kDiscard_ContentChangeMode
156 : SkSurface::kRetain_ContentChangeMode);
157 }
158}
159
160void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
161 ShaderOverrideOpacity overrideOpacity) {
162 if (fSurfaceBase) {
163 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
164 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
165 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
166 // and therefore we don't care which mode we're in.
167 //
168 if (fSurfaceBase->outstandingImageSnapshot()) {
169 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
170 mode = SkSurface::kDiscard_ContentChangeMode;
171 }
172 }
173 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000174 }
175}
176
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000179/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 The clip/matrix/proc are fields that reflect the top of the save/restore
181 stack. Whenever the canvas changes, it marks a dirty flag, and then before
182 these are used (assuming we're not on a layer) we rebuild these cache
183 values: they reflect the top of the save stack, but translated and clipped
184 by the device's XY offset and bitmap-bounds.
185*/
186struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400187 DeviceCM* fNext;
188 sk_sp<SkBaseDevice> fDevice;
189 SkRasterClip fClip;
190 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
191 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400192 sk_sp<SkImage> fClipImage;
193 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
Florin Malita53f77bd2017-04-28 13:48:37 -0400195 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000196 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700197 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400198 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500199 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700200 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000201 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400202 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400203 {}
reed@google.com4b226022011-01-11 18:32:13 +0000204
mtkleinfeaadee2015-04-08 11:25:48 -0700205 void reset(const SkIRect& bounds) {
206 SkASSERT(!fPaint);
207 SkASSERT(!fNext);
208 SkASSERT(fDevice);
209 fClip.setRect(bounds);
210 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211};
212
Mike Reed148b7fd2018-12-18 17:38:18 -0500213namespace {
214// Encapsulate state needed to restore from saveBehind()
215struct BackImage {
216 sk_sp<SkSpecialImage> fImage;
217 SkIPoint fLoc;
218};
219}
220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221/* This is the record we keep for each save/restore level in the stack.
222 Since a level optionally copies the matrix and/or stack, we have pointers
223 for these fields. If the value is copied for this level, the copy is
224 stored in the ...Storage field, and the pointer points to that. If the
225 value is not copied for this level, we ignore ...Storage, and just point
226 at the corresponding value in the previous level in the stack.
227*/
228class SkCanvas::MCRec {
229public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500230 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 /* If there are any layers in the stack, this points to the top-most
232 one that is at or below this level in the stack (so we know what
233 bitmap/device to draw into from this level. This value is NOT
234 reference counted, since the real owner is either our fLayer field,
235 or a previous one in a lower level.)
236 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500237 DeviceCM* fTopLayer;
238 std::unique_ptr<BackImage> fBackImage;
239 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500240 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500241 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
Mike Reeda1361362017-03-07 09:37:29 -0500243 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700244 fLayer = nullptr;
245 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800246 fMatrix.reset();
247 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700248
reedd9544982014-09-09 18:46:22 -0700249 // don't bother initializing fNext
250 inc_rec();
251 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400252 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700253 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700254 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800255 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 // don't bother initializing fNext
258 inc_rec();
259 }
260 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700261 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 dec_rec();
263 }
mtkleinfeaadee2015-04-08 11:25:48 -0700264
265 void reset(const SkIRect& bounds) {
266 SkASSERT(fLayer);
267 SkASSERT(fDeferredSaveCount == 0);
268
269 fMatrix.reset();
270 fRasterClip.setRect(bounds);
271 fLayer->reset(bounds);
272 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
Mike Reeda1361362017-03-07 09:37:29 -0500275class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276public:
Mike Reeda1361362017-03-07 09:37:29 -0500277 SkDrawIter(SkCanvas* canvas)
278 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
279 {}
reed@google.com4b226022011-01-11 18:32:13 +0000280
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000282 const DeviceCM* rec = fCurrLayer;
283 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400284 fDevice = rec->fDevice.get();
285 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700287 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 return true;
289 }
290 return false;
291 }
reed@google.com4b226022011-01-11 18:32:13 +0000292
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000294
Mike Reed99330ba2017-02-22 11:01:08 -0500295 SkBaseDevice* fDevice;
296
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 const DeviceCM* fCurrLayer;
299 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300};
301
Florin Malita713b8ef2017-04-28 10:57:24 -0400302#define FOR_EACH_TOP_DEVICE( code ) \
303 do { \
304 DeviceCM* layer = fMCRec->fTopLayer; \
305 while (layer) { \
306 SkBaseDevice* device = layer->fDevice.get(); \
307 if (device) { \
308 code; \
309 } \
310 layer = layer->fNext; \
311 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500312 } while (0)
313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314/////////////////////////////////////////////////////////////////////////////
315
reeddbc3cef2015-04-29 12:18:57 -0700316/**
317 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700318 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700319 */
reedd053ce92016-03-22 10:17:23 -0700320static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700321 SkImageFilter* imgf = paint.getImageFilter();
322 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700323 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700324 }
325
reedd053ce92016-03-22 10:17:23 -0700326 SkColorFilter* imgCFPtr;
327 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700328 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700329 }
reedd053ce92016-03-22 10:17:23 -0700330 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700331
332 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700333 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700334 // there is no existing paint colorfilter, so we can just return the imagefilter's
335 return imgCF;
336 }
337
338 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
339 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500340 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700341}
342
senorblanco87e066e2015-10-28 11:23:36 -0700343/**
344 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
345 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
346 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
347 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
348 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
349 * conservative "effective" bounds based on the settings in the paint... with one exception. This
350 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
351 * deliberately ignored.
352 */
353static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
354 const SkRect& rawBounds,
355 SkRect* storage) {
356 SkPaint tmpUnfiltered(paint);
357 tmpUnfiltered.setImageFilter(nullptr);
358 if (tmpUnfiltered.canComputeFastBounds()) {
359 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
360 } else {
361 return rawBounds;
362 }
363}
364
Mike Reed38992392019-07-30 10:48:15 -0400365class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366public:
senorblanco87e066e2015-10-28 11:23:36 -0700367 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
368 // paint. It's used to determine the size of the offscreen layer for filters.
369 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400370 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
371 bool skipLayerForImageFilter = false,
372 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400374 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700376 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
Mike Reed38992392019-07-30 10:48:15 -0400378 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
379 SkASSERT(!fLazyPaint.isValid());
380 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700381 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700382 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700383 fPaint = paint;
384 }
385
386 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700387 /**
388 * We implement ImageFilters for a given draw by creating a layer, then applying the
389 * imagefilter to the pixels of that layer (its backing surface/image), and then
390 * we call restore() to xfer that layer to the main canvas.
391 *
392 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
393 * 2. Generate the src pixels:
394 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
395 * return (fPaint). We then draw the primitive (using srcover) into a cleared
396 * buffer/surface.
397 * 3. Restore the layer created in #1
398 * The imagefilter is passed the buffer/surface from the layer (now filled with the
399 * src pixels of the primitive). It returns a new "filtered" buffer, which we
400 * draw onto the previous layer using the xfermode from the original paint.
401 */
Mike Reed38992392019-07-30 10:48:15 -0400402
403 SkPaint restorePaint;
404 restorePaint.setImageFilter(fPaint->refImageFilter());
405 restorePaint.setBlendMode(fPaint->getBlendMode());
406
senorblanco87e066e2015-10-28 11:23:36 -0700407 SkRect storage;
408 if (rawBounds) {
409 // Make rawBounds include all paint outsets except for those due to image filters.
410 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
411 }
Mike Reed38992392019-07-30 10:48:15 -0400412 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700413 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700414 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000415
Mike Reed38992392019-07-30 10:48:15 -0400416 // Remove the restorePaint fields from our "working" paint
417 SkASSERT(!fLazyPaint.isValid());
418 SkPaint* paint = fLazyPaint.set(origPaint);
419 paint->setImageFilter(nullptr);
420 paint->setBlendMode(SkBlendMode::kSrcOver);
421 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000422 }
423 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000424
Mike Reed38992392019-07-30 10:48:15 -0400425 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700426 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000427 fCanvas->internalRestore();
428 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000429 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000431
reed@google.com4e2b3d32011-04-07 14:18:59 +0000432 const SkPaint& paint() const {
433 SkASSERT(fPaint);
434 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437private:
Mike Reed38992392019-07-30 10:48:15 -0400438 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000439 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400440 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700442 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443};
444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445////////// macros to place around the internal draw calls //////////////////
446
Mike Reed38992392019-07-30 10:48:15 -0400447#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700448 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400449 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
450 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800451
452
Mike Reed38992392019-07-30 10:48:15 -0400453#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000454 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400455 AutoLayerForImageFilter draw(this, paint, true); \
456 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000457
Mike Reed38992392019-07-30 10:48:15 -0400458#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000459 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400460 AutoLayerForImageFilter draw(this, paint, false, bounds); \
461 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000462
Mike Reed38992392019-07-30 10:48:15 -0400463#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700464 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400465 AutoLayerForImageFilter draw(this, paint, false, bounds); \
466 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700467
Mike Reed38992392019-07-30 10:48:15 -0400468#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469
470////////////////////////////////////////////////////////////////////////////
471
msarettfbfa2582016-08-12 08:29:08 -0700472static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
473 if (bounds.isEmpty()) {
474 return SkRect::MakeEmpty();
475 }
476
477 // Expand bounds out by 1 in case we are anti-aliasing. We store the
478 // bounds as floats to enable a faster quick reject implementation.
479 SkRect dst;
480 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
481 return dst;
482}
483
mtkleinfeaadee2015-04-08 11:25:48 -0700484void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
485 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700486 fMCRec->reset(bounds);
487
488 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500489 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400490 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700491 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700492 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700493}
494
Hal Canary363a3f82018-10-04 11:04:48 -0400495void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800496 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497
498 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500499 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500500 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700501 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
reeda499f902015-05-01 09:34:31 -0700503 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
504 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400505 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508
halcanary96fcdcc2015-08-27 07:41:13 -0700509 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000510
reedf92c8662014-08-18 08:02:43 -0700511 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700512 // The root device and the canvas should always have the same pixel geometry
513 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800514 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700515 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500516
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700518 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400519
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500520 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521}
522
reed@google.comcde92112011-07-06 20:00:52 +0000523SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000524 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700525 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000527 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000528
Hal Canary363a3f82018-10-04 11:04:48 -0400529 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000530}
531
reed96a857e2015-01-25 10:33:58 -0800532SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800534 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535{
536 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400537 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500538 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700539}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000540
Hal Canary363a3f82018-10-04 11:04:48 -0400541SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700542 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700543 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700544{
545 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700546
Mike Reed566e53c2017-03-10 10:49:45 -0500547 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400548 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700549}
550
Herb Derbyefe39bc2018-05-01 17:06:20 -0400551SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000552 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700553 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700556
Hal Canary363a3f82018-10-04 11:04:48 -0400557 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700558}
559
reed4a8126e2014-09-22 07:29:03 -0700560SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700561 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700562 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700563{
564 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700565
Mike Reed910ca0f2018-04-25 13:04:05 -0400566 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400567 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700568}
reed29c857d2014-09-21 10:25:07 -0700569
Mike Reed356f7c22017-01-10 11:58:39 -0500570SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
571 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700572 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
573 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500574 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700575{
576 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700577
Mike Reed910ca0f2018-04-25 13:04:05 -0400578 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400579 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580}
581
Mike Reed356f7c22017-01-10 11:58:39 -0500582SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
583
Matt Sarett31f99ce2017-04-11 08:46:01 -0400584#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
585SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
586 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
587 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
588 , fAllocator(nullptr)
589{
590 inc_canvas();
591
592 SkBitmap tmp(bitmap);
593 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400594 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400595 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400596}
597#endif
598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599SkCanvas::~SkCanvas() {
600 // free up the contents of our deque
601 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 this->internalRestore(); // restore the last, since we're going away
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 dec_canvas();
606}
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608///////////////////////////////////////////////////////////////////////////////
609
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000610void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700611 this->onFlush();
612}
613
614void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000615 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000616 if (device) {
617 device->flush();
618 }
619}
620
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500621SkSurface* SkCanvas::getSurface() const {
622 return fSurfaceBase;
623}
624
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000625SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000626 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000627 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
628}
629
senorblancoafc7cce2016-02-02 18:44:15 -0800630SkIRect SkCanvas::getTopLayerBounds() const {
631 SkBaseDevice* d = this->getTopDevice();
632 if (!d) {
633 return SkIRect::MakeEmpty();
634 }
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500635 return d->getGlobalBounds();
senorblancoafc7cce2016-02-02 18:44:15 -0800636}
637
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000638SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000640 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400642 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643}
644
Florin Malita0ed3b642017-01-13 16:56:38 +0000645SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400646 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000647}
648
Mike Reed353196f2017-07-21 11:01:18 -0400649bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000650 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400651 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000652}
653
Mike Reed353196f2017-07-21 11:01:18 -0400654bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
655 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400656}
657
658bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
659 SkPixmap pm;
660 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
661}
662
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000663bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400664 SkPixmap pm;
665 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700666 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000667 }
668 return false;
669}
670
Matt Sarett03dd6d52017-01-23 12:15:09 -0500671bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000672 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 SkBaseDevice* device = this->getDevice();
674 if (!device) {
675 return false;
676 }
677
Matt Sarett03dd6d52017-01-23 12:15:09 -0500678 // This check gives us an early out and prevents generation ID churn on the surface.
679 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
680 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400681 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500682 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000683 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684
Matt Sarett03dd6d52017-01-23 12:15:09 -0500685 // Tell our owning surface to bump its generation ID.
686 const bool completeOverwrite =
687 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700688 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700689
Matt Sarett03dd6d52017-01-23 12:15:09 -0500690 // This can still fail, most notably in the case of a invalid color type or alpha type
691 // conversion. We could pull those checks into this function and avoid the unnecessary
692 // generation ID bump. But then we would be performing those checks twice, since they
693 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400694 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000695}
reed@google.com51df9e32010-12-23 19:29:18 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697//////////////////////////////////////////////////////////////////////////////
698
reed2ff1fce2014-12-11 07:07:37 -0800699void SkCanvas::checkForDeferredSave() {
700 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800701 this->doSave();
702 }
703}
704
reedf0090cb2014-11-26 08:55:51 -0800705int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800706#ifdef SK_DEBUG
707 int count = 0;
708 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
709 for (;;) {
710 const MCRec* rec = (const MCRec*)iter.next();
711 if (!rec) {
712 break;
713 }
714 count += 1 + rec->fDeferredSaveCount;
715 }
716 SkASSERT(count == fSaveCount);
717#endif
718 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800719}
720
721int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800722 fSaveCount += 1;
723 fMCRec->fDeferredSaveCount += 1;
724 return this->getSaveCount() - 1; // return our prev value
725}
726
727void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800728 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700729
730 SkASSERT(fMCRec->fDeferredSaveCount > 0);
731 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800732 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800733}
734
Mike Reed00a97642020-01-25 18:42:23 -0500735int SkCanvas::experimental_saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500736 // TODO: add a virtual for this, and update clients (e.g. chrome)
737 int n = this->save();
Mike Reed46f5c5f2020-02-20 15:42:29 -0500738 this->concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500739 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500740 return n;
741}
742
Mike Reed00a97642020-01-25 18:42:23 -0500743int SkCanvas::experimental_saveCamera(const SkScalar projection[16],
744 const SkScalar camera[16]) {
745 SkM44 proj, cam;
746 proj.setColMajor(projection);
747 cam.setColMajor(camera);
748 return this->experimental_saveCamera(proj, cam);
749}
750
reedf0090cb2014-11-26 08:55:51 -0800751void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800752 if (fMCRec->fDeferredSaveCount > 0) {
753 SkASSERT(fSaveCount > 1);
754 fSaveCount -= 1;
755 fMCRec->fDeferredSaveCount -= 1;
756 } else {
757 // check for underflow
758 if (fMCStack.count() > 1) {
759 this->willRestore();
760 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700761 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800762 this->internalRestore();
763 this->didRestore();
764 }
reedf0090cb2014-11-26 08:55:51 -0800765 }
766}
767
768void SkCanvas::restoreToCount(int count) {
769 // sanity check
770 if (count < 1) {
771 count = 1;
772 }
mtkleinf0f14112014-12-12 08:46:25 -0800773
reedf0090cb2014-11-26 08:55:51 -0800774 int n = this->getSaveCount() - count;
775 for (int i = 0; i < n; ++i) {
776 this->restore();
777 }
778}
779
reed2ff1fce2014-12-11 07:07:37 -0800780void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700782 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000784
Mike Reedc42a1cd2017-02-14 14:25:14 -0500785 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786}
787
reed4960eee2015-12-18 07:09:18 -0800788bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400789 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790}
791
reed4960eee2015-12-18 07:09:18 -0800792bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700793 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400794 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
795 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
796 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
797 // filter does not require a decomposed CTM matrix, the filter space and device space are the
798 // same. When it has been decomposed, we want the original image filter node to process the
799 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
800 // longer the original filter, but has the remainder matrix baked into it, and passing in the
801 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
802 // to the original filter node (barring inflation from consecutive calls to mapRect). While
803 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
804 // passing getDeviceClipBounds() to 'imageFilter' is correct.
805 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
806 // be important to more accurately calculate the clip bounds in the layer space for the original
807 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500808 SkIRect clipBounds = this->getDeviceClipBounds();
809 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000811 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000812
reed96e657d2015-03-10 17:30:07 -0700813 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
814
Robert Phillips12078432018-05-17 11:17:39 -0400815 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
816 // If the image filter DAG affects transparent black then we will need to render
817 // out to the clip bounds
818 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000819 }
Robert Phillips12078432018-05-17 11:17:39 -0400820
821 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700822 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700824 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400825 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400827 inputSaveLayerBounds = clipBounds;
828 }
829
830 if (imageFilter) {
831 // expand the clip bounds by the image filter DAG to include extra content that might
832 // be required by the image filters.
833 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
834 SkImageFilter::kReverse_MapDirection,
835 &inputSaveLayerBounds);
836 }
837
838 SkIRect clippedSaveLayerBounds;
839 if (bounds) {
840 // For better or for worse, user bounds currently act as a hard clip on the layer's
841 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
842 clippedSaveLayerBounds = inputSaveLayerBounds;
843 } else {
844 // If there are no user bounds, we don't want to artificially restrict the resulting
845 // layer bounds, so allow the expanded clip bounds free reign.
846 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800848
849 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400850 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800851 if (BoundsAffectsClip(saveLayerFlags)) {
852 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
853 fMCRec->fRasterClip.setEmpty();
854 fDeviceClipBounds.setEmpty();
855 }
856 return false;
857 }
Robert Phillips12078432018-05-17 11:17:39 -0400858 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859
reed4960eee2015-12-18 07:09:18 -0800860 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700861 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400862 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
863 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000864 }
865
866 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400867 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000868 }
Robert Phillips12078432018-05-17 11:17:39 -0400869
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870 return true;
871}
872
reed4960eee2015-12-18 07:09:18 -0800873int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
874 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000875}
876
Cary Clarke041e312018-03-06 13:00:52 -0500877int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700878 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400879 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
880 // no need for the layer (or any of the draws until the matching restore()
881 this->save();
882 this->clipRect({0,0,0,0});
883 } else {
884 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
885 fSaveCount += 1;
886 this->internalSaveLayer(rec, strategy);
887 }
reed4960eee2015-12-18 07:09:18 -0800888 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800889}
890
Mike Reed148b7fd2018-12-18 17:38:18 -0500891int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
892 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
893 // Assuming clips never expand, if the request bounds is outside of the current clip
894 // there is no need to copy/restore the area, so just devolve back to a regular save.
895 this->save();
896 } else {
897 bool doTheWork = this->onDoSaveBehind(bounds);
898 fSaveCount += 1;
899 this->internalSave();
900 if (doTheWork) {
901 this->internalSaveBehind(bounds);
902 }
903 }
904 return this->getSaveCount() - 1;
905}
906
reeda2217ef2016-07-20 06:04:34 -0700907void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500908 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500909 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400910 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
911 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400912 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwigfb3f3022020-02-20 13:23:58 -0500913 // TODO(michaelludwig) - Update this function to use the relative transforms between src and
914 // dst; for now, since devices never have complex transforms, we can keep using getOrigin().
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400915 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400916 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
917 // This means that we only have to copy a dst-sized block of pixels out of src and translate
918 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400919 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
920 dstOrigin.y() - src->getOrigin().y(),
921 dst->width(), dst->height());
922 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400923 return;
924 }
925
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400926 auto special = src->snapSpecial(snapBounds);
927 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400928 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
929 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400930 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
931 }
932 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400933 }
934
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400935 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
936 // by the backdrop filter.
937 SkMatrix toRoot, layerMatrix;
938 SkSize scale;
939 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
940 toRoot = SkMatrix::I();
941 layerMatrix = ctm;
942 } else if (ctm.decomposeScale(&scale, &toRoot)) {
943 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
944 } else {
945 // Perspective, for now, do no scaling of the layer itself.
946 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
947 // the matrix, e.g. based on the midpoint of the near/far planes?
948 toRoot = ctm;
949 layerMatrix = SkMatrix::I();
950 }
951
952 // We have to map the dst bounds from the root space into the layer space where filtering will
953 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
954 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
955 // is a safe, conservative estimate.
956 SkMatrix fromRoot;
957 if (!toRoot.invert(&fromRoot)) {
958 return;
959 }
960
961 // This represents what the backdrop filter needs to produce in the layer space, and is sized
962 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
963 SkIRect layerTargetBounds = fromRoot.mapRect(
964 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
965 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
966 // require some extra input pixels.
967 SkIRect layerInputBounds = filter->filterBounds(
968 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
969 &layerTargetBounds);
970
971 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400972 // be the conservative contents required to fill a layerInputBounds-sized surface with the
973 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400974 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
975 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
976 if (!backdropBounds.intersect(srcDevRect)) {
977 return;
978 }
979
980 auto special = src->snapSpecial(backdropBounds);
981 if (!special) {
982 return;
983 }
984
985 SkColorType colorType = src->imageInfo().colorType();
986 if (colorType == kUnknown_SkColorType) {
987 colorType = kRGBA_8888_SkColorType;
988 }
989 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400990
991 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400992 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400993 // Drawing the temporary and final filtered image requires a higher filter quality if the
994 // 'toRoot' transformation is not identity, in order to minimize the impact on already
995 // rendered edges/content.
996 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
997 p.setFilterQuality(kHigh_SkFilterQuality);
998
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400999 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1000 // and stored in a temporary surface, which is then used as the input to the actual filter.
1001 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1002 if (!tmpSurface) {
1003 return;
1004 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001005
1006 auto tmpCanvas = tmpSurface->getCanvas();
1007 tmpCanvas->clear(SK_ColorTRANSPARENT);
1008 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1009 // space, then maps from root space into the layer space, then maps it so the input layer's
1010 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1011 // performed on backdropBounds.
1012 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1013 tmpCanvas->concat(fromRoot);
1014 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001015
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001016 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1017 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1018 special = tmpSurface->makeImageSnapshot();
1019 } else {
1020 // Since there is no extra transform that was done, update the input bounds to reflect
1021 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1022 // was equal to backdropBounds before it was made relative to the src device and cropped.
1023 // When we use the original snapped image directly, just map the update backdrop bounds
1024 // back into the shared layer space
1025 layerInputBounds = backdropBounds;
1026 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001027
1028 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1029 // draw will be 1-1 so there is no need to increase filter quality.
1030 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001031 }
1032
1033 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1034 // layer space. This has to further offset everything so that filter evaluation thinks the
1035 // source image's top left corner is (0, 0).
1036 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1037 // this can be simplified.
1038 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1039 SkMatrix filterCTM = layerMatrix;
1040 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1041 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1042
1043 SkIPoint offset;
1044 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001045 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001046 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1047 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1048 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1049 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050 offset += layerInputBounds.topLeft();
1051
1052 // Manually setting the device's CTM requires accounting for the device's origin.
1053 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001054 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001055 // a global CTM instead of a device CTM.
1056 SkMatrix dstCTM = toRoot;
1057 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001058 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001059
1060 // And because devices don't have a special-image draw function that supports arbitrary
1061 // matrices, we are abusing the asImage() functionality here...
1062 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001063 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001064 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001065 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001066 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1067 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001068 }
robertphillips7354a4b2015-12-16 05:08:27 -08001069}
reed70ee31b2015-12-10 13:44:45 -08001070
Mike Kleine083f7c2018-02-07 12:54:27 -05001071static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001072 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001073 if (prev.bytesPerPixel() <= 4 &&
1074 prev.colorType() != kRGBA_8888_SkColorType &&
1075 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001076 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1077 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1078 ct = kN32_SkColorType;
1079 }
1080 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001081}
1082
reed4960eee2015-12-18 07:09:18 -08001083void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001084 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001085 const SkRect* bounds = rec.fBounds;
1086 const SkPaint* paint = rec.fPaint;
1087 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1088
Mike Reed5532c2a2019-02-23 12:00:32 -05001089 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1090 // regardless of any hint-rect from the caller. skbug.com/8783
1091 if (rec.fBackdrop) {
1092 bounds = nullptr;
1093 }
1094
reed8c30a812016-04-20 16:36:51 -07001095 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001096 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001097 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001098 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001099
reed8c30a812016-04-20 16:36:51 -07001100 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001101 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1102 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1103 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001104 *
1105 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001106 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1107 * if necessary.
1108 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1109 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001110 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
Michael Ludwig08b260c2019-05-17 11:21:53 -04001111 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001112 * of the original imagefilter, and draw that (via drawSprite)
1113 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1114 *
1115 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1116 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1117 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001118 if (imageFilter) {
1119 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001120 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1121 &modifiedCTM);
1122 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001123 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001124 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001125 modifiedRec = fMCRec;
1126 this->internalSetMatrix(modifiedCTM);
1127 SkPaint* p = lazyP.set(*paint);
1128 p->setImageFilter(std::move(modifiedFilter));
1129 imageFilter = p->getImageFilter();
1130 paint = p;
1131 }
1132 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1133 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001134 }
reed8c30a812016-04-20 16:36:51 -07001135
junov@chromium.orga907ac32012-02-24 21:54:07 +00001136 // do this before we create the layer. We don't call the public save() since
1137 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001138 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001139
junov@chromium.orga907ac32012-02-24 21:54:07 +00001140 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001141 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001142 if (modifiedRec) {
1143 // In this case there will be no layer in which to stash the matrix so we need to
1144 // revert the prior MCRec to its earlier state.
1145 modifiedRec->fMatrix = stashedMatrix;
1146 }
reed2ff1fce2014-12-11 07:07:37 -08001147 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 }
1149
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001150 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1151 // the clipRectBounds() call above?
1152 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001153 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001154 }
1155
reed8dc0ccb2015-03-20 06:32:52 -07001156 SkPixelGeometry geo = fProps.pixelGeometry();
1157 if (paint) {
reed76033be2015-03-14 10:54:31 -07001158 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001159 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001160 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001161 }
1162 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163
robertphillips5139e502016-07-19 05:10:40 -07001164 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001165 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001166 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001167 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001168 }
reedb2db8982014-11-13 12:41:02 -08001169
Mike Kleine083f7c2018-02-07 12:54:27 -05001170 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001171 if (rec.fSaveLayerFlags & kF16ColorType) {
1172 info = info.makeColorType(kRGBA_F16_SkColorType);
1173 }
reed129ed1c2016-02-22 06:42:31 -08001174
Hal Canary704cd322016-11-07 14:13:52 -05001175 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001176 {
Florin Malita4571e492019-07-16 10:25:58 -04001177 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001178 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001179 const bool trackCoverage =
1180 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001181 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001182 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001183 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001184 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1185 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001186 return;
reed61f501f2015-04-29 08:34:00 -07001187 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001188 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001189 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190
Mike Reedb43a3e02017-02-11 10:18:58 -05001191 // only have a "next" if this new layer doesn't affect the clip (rare)
1192 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 fMCRec->fLayer = layer;
1194 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001195
Mike Reedc61abee2017-02-28 17:45:27 -05001196 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001197 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001198 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001199 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001200
Mike Reedc42a1cd2017-02-14 14:25:14 -05001201 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1202
1203 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1204 if (layer->fNext) {
1205 // need to punch a hole in the previous device, so we don't draw there, given that
1206 // the new top-layer will allow drawing to happen "below" it.
1207 SkRegion hole(ir);
1208 do {
1209 layer = layer->fNext;
1210 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1211 } while (layer->fNext);
1212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213}
1214
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001215int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001216 if (0xFF == alpha) {
1217 return this->saveLayer(bounds, nullptr);
1218 } else {
1219 SkPaint tmpPaint;
1220 tmpPaint.setAlpha(alpha);
1221 return this->saveLayer(bounds, &tmpPaint);
1222 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001223}
1224
Mike Reed148b7fd2018-12-18 17:38:18 -05001225void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Mike Reed148b7fd2018-12-18 17:38:18 -05001226 SkBaseDevice* device = this->getTopDevice();
1227 if (nullptr == device) { // Do we still need this check???
1228 return;
1229 }
1230
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001231 // Map the local bounds into the top device's coordinate space (this is not
1232 // necessarily the full global CTM transform).
1233 SkIRect devBounds;
1234 if (localBounds) {
1235 SkRect tmp;
1236 device->localToDevice().mapRect(&tmp, *localBounds);
1237 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1238 devBounds.setEmpty();
1239 }
1240 } else {
1241 devBounds = device->devClipBounds();
1242 }
1243 if (devBounds.isEmpty()) {
1244 return;
1245 }
Mike Reed148b7fd2018-12-18 17:38:18 -05001246
Michael Ludwigac352122019-08-28 21:03:05 +00001247 // This is getting the special image from the current device, which is then drawn into (both by
1248 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1249 // own device, we need to explicitly copy the back image contents so that its original content
1250 // is available when we splat it back later during restore.
1251 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001252 if (!backImage) {
1253 return;
1254 }
1255
1256 // we really need the save, so we can wack the fMCRec
1257 this->checkForDeferredSave();
1258
1259 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1260
1261 SkPaint paint;
1262 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001263 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001264}
1265
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266void SkCanvas::internalRestore() {
1267 SkASSERT(fMCStack.count() != 0);
1268
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001269 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 DeviceCM* layer = fMCRec->fLayer; // may be null
1271 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001272 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273
Mike Reed148b7fd2018-12-18 17:38:18 -05001274 // move this out before we do the actual restore
1275 auto backImage = std::move(fMCRec->fBackImage);
1276
Mike Reedb18e74d2020-01-16 13:58:22 -05001277 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1278 fCameraStack.pop_back();
1279 }
1280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 // now do the normal restore()
1282 fMCRec->~MCRec(); // balanced in save()
1283 fMCStack.pop_back();
1284 fMCRec = (MCRec*)fMCStack.back();
1285
Mike Reedc42a1cd2017-02-14 14:25:14 -05001286 if (fMCRec) {
1287 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1288 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001289
Mike Reed148b7fd2018-12-18 17:38:18 -05001290 if (backImage) {
1291 SkPaint paint;
1292 paint.setBlendMode(SkBlendMode::kDstOver);
1293 const int x = backImage->fLoc.x();
1294 const int y = backImage->fLoc.y();
1295 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1296 nullptr, SkMatrix::I());
1297 }
1298
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1300 since if we're being recorded, we don't want to record this (the
1301 recorder will have already recorded the restore).
1302 */
bsalomon49f085d2014-09-05 13:34:00 -07001303 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001304 if (fMCRec) {
Lee Salzman5d8949c2018-09-19 15:36:54 -04001305 layer->fDevice->setImmutable();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001306 // At this point, 'layer' has been removed from the device stack, so the devices that
1307 // internalDrawDevice sees are the destinations that 'layer' is drawn into.
1308 this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001309 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001310 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001311 this->internalSetMatrix(layer->fStashedMatrix);
halcanary385fe4d2015-08-26 13:07:48 -07001312 delete layer;
reedb679ca82015-04-07 04:40:48 -07001313 } else {
1314 // we're at the root
reeda499f902015-05-01 09:34:31 -07001315 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001316 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001317 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001319 }
msarettfbfa2582016-08-12 08:29:08 -07001320
1321 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001322 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001323 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325}
1326
reede8f30622016-03-23 18:59:25 -07001327sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001328 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001329 props = &fProps;
1330 }
1331 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001332}
1333
reede8f30622016-03-23 18:59:25 -07001334sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001335 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001336 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001337}
1338
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001339SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001340 return this->onImageInfo();
1341}
1342
1343SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001344 SkBaseDevice* dev = this->getDevice();
1345 if (dev) {
1346 return dev->imageInfo();
1347 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001348 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001349 }
1350}
1351
brianosman898235c2016-04-06 07:38:23 -07001352bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001353 return this->onGetProps(props);
1354}
1355
1356bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001357 SkBaseDevice* dev = this->getDevice();
1358 if (dev) {
1359 if (props) {
1360 *props = fProps;
1361 }
1362 return true;
1363 } else {
1364 return false;
1365 }
1366}
1367
reed6ceeebd2016-03-09 14:26:26 -08001368bool SkCanvas::peekPixels(SkPixmap* pmap) {
1369 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001370}
1371
reed884e97c2015-05-26 11:31:54 -07001372bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001374 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375}
1376
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001377void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001378 SkPixmap pmap;
1379 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001380 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001381 }
1382 if (info) {
1383 *info = pmap.info();
1384 }
1385 if (rowBytes) {
1386 *rowBytes = pmap.rowBytes();
1387 }
1388 if (origin) {
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001389 // If the caller requested the origin, they presumably are expecting the returned pixels to
1390 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1391 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1392 // instead of an origin, just don't expose the pixels in that case. Note that this means
1393 // that layers with complex coordinate spaces can still report their pixels if the caller
1394 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1395 if (this->getTopDevice()->isPixelAlignedToGlobal()) {
1396 *origin = this->getTopDevice()->getOrigin();
1397 } else {
1398 return nullptr;
1399 }
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001400 }
reed884e97c2015-05-26 11:31:54 -07001401 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001402}
1403
reed884e97c2015-05-26 11:31:54 -07001404bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001405 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001406 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001407}
1408
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
Mike Reed8bcd1282019-03-13 16:51:54 -04001411// In our current design/features, we should never have a layer (src) in a different colorspace
1412// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1413// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1414// colorspace.
1415static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1416 SkASSERT(src == dst);
1417}
1418
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001419void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001420 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001422 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 paint = &tmp;
1424 }
reed@google.com4b226022011-01-11 18:32:13 +00001425
Mike Reed38992392019-07-30 10:48:15 -04001426 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001429 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001430 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1431 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001432 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001433 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05001434 // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet,
1435 // so it should always be possible to use the relative origin. Once drawDevice() and
1436 // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead.
1437 SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin();
Florin Malita53f77bd2017-04-28 13:48:37 -04001438 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001439 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1440 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001441 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1442 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001443 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1444 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001445 }
reed@google.com76dd2772012-01-05 21:15:07 +00001446 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001447 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001448 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 }
reeda2217ef2016-07-20 06:04:34 -07001450
Mike Reed38992392019-07-30 10:48:15 -04001451 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452}
1453
reed32704672015-12-16 08:27:10 -08001454/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001455
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001456void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001457 if (dx || dy) {
1458 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001459 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001460
reedfe69b502016-09-12 06:31:48 -07001461 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001462 // However, if either is non-finite, we might still complicate the matrix type,
1463 // so we still have to compute this.
1464 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001465
Mike Reedc42a1cd2017-02-14 14:25:14 -05001466 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001467
reedfe69b502016-09-12 06:31:48 -07001468 this->didTranslate(dx,dy);
1469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001472void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001473 if (sx != 1 || sy != 1) {
1474 this->checkForDeferredSave();
1475 fMCRec->fMatrix.preScale(sx, sy);
1476
1477 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1478 // but pre-scaling by a non-finite does change it, so we have to recompute.
1479 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1480
1481 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1482
1483 this->didScale(sx, sy);
1484 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485}
1486
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001487void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001488 SkMatrix m;
1489 m.setRotate(degrees);
1490 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491}
1492
bungeman7438bfc2016-07-12 15:01:19 -07001493void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1494 SkMatrix m;
1495 m.setRotate(degrees, px, py);
1496 this->concat(m);
1497}
1498
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001499void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001500 SkMatrix m;
1501 m.setSkew(sx, sy);
1502 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001503}
1504
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001505void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001506 if (matrix.isIdentity()) {
1507 return;
1508 }
1509
reed2ff1fce2014-12-11 07:07:37 -08001510 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001511 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001512
msarett9637ea92016-08-18 14:03:30 -07001513 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001514
Mike Reed7627fa52017-02-08 10:07:53 -05001515 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001516
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001517 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001518}
1519
Mike Reed46f5c5f2020-02-20 15:42:29 -05001520void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001521 this->checkForDeferredSave();
1522
Mike Reed46f5c5f2020-02-20 15:42:29 -05001523 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001524
1525 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1526
1527 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1528
Mike Reed46f5c5f2020-02-20 15:42:29 -05001529 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001530}
Mike Reed064c7f92020-01-08 17:33:04 -05001531
Mike Reed46f5c5f2020-02-20 15:42:29 -05001532void SkCanvas::concat44(const SkM44& m) {
1533 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001534}
1535
reed8c30a812016-04-20 16:36:51 -07001536void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001537 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001538 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001539
Mike Reedc42a1cd2017-02-14 14:25:14 -05001540 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001541}
1542
1543void SkCanvas::setMatrix(const SkMatrix& matrix) {
1544 this->checkForDeferredSave();
1545 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001546 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547}
1548
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001550 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551}
1552
1553//////////////////////////////////////////////////////////////////////////////
1554
Mike Reedc1f77742016-12-09 09:00:50 -05001555void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001556 if (!rect.isFinite()) {
1557 return;
1558 }
reed2ff1fce2014-12-11 07:07:37 -08001559 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001560 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1561 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562}
1563
Mike Reedc1f77742016-12-09 09:00:50 -05001564void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001565 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001566
Mike Reed7627fa52017-02-08 10:07:53 -05001567 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001568
reedc64eff52015-11-21 12:39:45 -08001569 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001570 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1571 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001572 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573}
1574
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001575void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1576 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001577 if (fClipRestrictionRect.isEmpty()) {
1578 // we notify the device, but we *dont* resolve deferred saves (since we're just
1579 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001580 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001581 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001582 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001583 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001584 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001585 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001586 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1587 }
1588}
1589
Mike Reedc1f77742016-12-09 09:00:50 -05001590void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001591 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001592 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001593 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1595 } else {
1596 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001597 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001599
Mike Reedc1f77742016-12-09 09:00:50 -05001600void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001601 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001602
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001604
Mike Reed7627fa52017-02-08 10:07:53 -05001605 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001606
Mike Reed20800c82017-11-15 16:09:04 -05001607 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1608 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001609 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001610}
1611
Mike Reedc1f77742016-12-09 09:00:50 -05001612void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001613 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001615
1616 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1617 SkRect r;
1618 if (path.isRect(&r)) {
1619 this->onClipRect(r, op, edgeStyle);
1620 return;
1621 }
1622 SkRRect rrect;
1623 if (path.isOval(&r)) {
1624 rrect.setOval(r);
1625 this->onClipRRect(rrect, op, edgeStyle);
1626 return;
1627 }
1628 if (path.isRRect(&rrect)) {
1629 this->onClipRRect(rrect, op, edgeStyle);
1630 return;
1631 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001632 }
robertphillips39f05382015-11-24 09:30:12 -08001633
1634 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635}
1636
Mike Reedc1f77742016-12-09 09:00:50 -05001637void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001638 AutoValidateClip avc(this);
1639
Brian Salomona3b45d42016-10-03 11:36:16 -04001640 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001641
Mike Reed7627fa52017-02-08 10:07:53 -05001642 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643
Brian Salomona3b45d42016-10-03 11:36:16 -04001644 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001645 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001646 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001647 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648}
1649
Mike Reedc1f77742016-12-09 09:00:50 -05001650void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001651 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001652 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653}
1654
Mike Reedc1f77742016-12-09 09:00:50 -05001655void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001656 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001657
reed@google.com5c3d1472011-02-22 19:12:23 +00001658 AutoValidateClip avc(this);
1659
Mike Reed20800c82017-11-15 16:09:04 -05001660 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001661 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662}
1663
reed@google.com819c9212011-02-23 18:56:55 +00001664#ifdef SK_DEBUG
1665void SkCanvas::validateClip() const {
1666 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001667 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001668 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001669 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001670 return;
1671 }
reed@google.com819c9212011-02-23 18:56:55 +00001672}
1673#endif
1674
Mike Reeda1361362017-03-07 09:37:29 -05001675bool SkCanvas::androidFramework_isClipAA() const {
1676 bool containsAA = false;
1677
1678 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1679
1680 return containsAA;
1681}
1682
1683class RgnAccumulator {
1684 SkRegion* fRgn;
1685public:
1686 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1687 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1688 SkIPoint origin = device->getOrigin();
1689 if (origin.x() | origin.y()) {
1690 rgn->translate(origin.x(), origin.y());
1691 }
1692 fRgn->op(*rgn, SkRegion::kUnion_Op);
1693 }
1694};
1695
1696void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1697 RgnAccumulator accum(rgn);
1698 SkRegion tmp;
1699
1700 rgn->setEmpty();
1701 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001702}
1703
reed@google.com5c3d1472011-02-22 19:12:23 +00001704///////////////////////////////////////////////////////////////////////////////
1705
reed@google.com754de5f2014-02-24 19:38:20 +00001706bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001707 return fMCRec->fRasterClip.isEmpty();
1708
1709 // TODO: should we only use the conservative answer in a recording canvas?
1710#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001711 SkBaseDevice* dev = this->getTopDevice();
1712 // if no device we return true
1713 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001714#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001715}
1716
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001717bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001718 SkBaseDevice* dev = this->getTopDevice();
1719 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001720 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001721}
1722
msarettfbfa2582016-08-12 08:29:08 -07001723static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1724#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1725 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1726 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1727 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1728 return 0xF != _mm_movemask_ps(mask);
1729#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1730 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1731 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1732 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1733 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1734#else
1735 SkRect devRectAsRect;
1736 SkRect devClipAsRect;
1737 devRect.store(&devRectAsRect.fLeft);
1738 devClip.store(&devClipAsRect.fLeft);
1739 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1740#endif
1741}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001742
msarettfbfa2582016-08-12 08:29:08 -07001743// It's important for this function to not be inlined. Otherwise the compiler will share code
1744// between the fast path and the slow path, resulting in two slow paths.
1745static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1746 const SkMatrix& matrix) {
1747 SkRect deviceRect;
1748 matrix.mapRect(&deviceRect, src);
1749 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1750}
1751
1752bool SkCanvas::quickReject(const SkRect& src) const {
1753#ifdef SK_DEBUG
1754 // Verify that fDeviceClipBounds are set properly.
1755 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001756 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001757 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001758 } else {
msarettfbfa2582016-08-12 08:29:08 -07001759 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760 }
msarettfbfa2582016-08-12 08:29:08 -07001761
msarett9637ea92016-08-18 14:03:30 -07001762 // Verify that fIsScaleTranslate is set properly.
1763 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001764#endif
1765
msarett9637ea92016-08-18 14:03:30 -07001766 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001767 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1768 }
1769
1770 // We inline the implementation of mapScaleTranslate() for the fast path.
1771 float sx = fMCRec->fMatrix.getScaleX();
1772 float sy = fMCRec->fMatrix.getScaleY();
1773 float tx = fMCRec->fMatrix.getTranslateX();
1774 float ty = fMCRec->fMatrix.getTranslateY();
1775 Sk4f scale(sx, sy, sx, sy);
1776 Sk4f trans(tx, ty, tx, ty);
1777
1778 // Apply matrix.
1779 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1780
1781 // Make sure left < right, top < bottom.
1782 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1783 Sk4f min = Sk4f::Min(ltrb, rblt);
1784 Sk4f max = Sk4f::Max(ltrb, rblt);
1785 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1786 // ARM this sequence generates the fastest (a single instruction).
1787 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1788
1789 // Check if the device rect is NaN or outside the clip.
1790 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791}
1792
reed@google.com3b3e8952012-08-16 20:53:31 +00001793bool SkCanvas::quickReject(const SkPath& path) const {
1794 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795}
1796
Mike Klein83c8dd92017-11-28 17:08:45 -05001797SkRect SkCanvas::getLocalClipBounds() const {
1798 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001799 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001800 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801 }
1802
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001803 SkMatrix inverse;
1804 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001805 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001806 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001807 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808
Mike Reed42e8c532017-01-23 14:09:13 -05001809 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001810 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001811 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001812
Mike Reedb57b9312018-04-23 12:12:54 -04001813 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001814 inverse.mapRect(&bounds, r);
1815 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816}
1817
Mike Klein83c8dd92017-11-28 17:08:45 -05001818SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001819 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001820}
1821
Mike Reedb18e74d2020-01-16 13:58:22 -05001822///////////////////////////////////////////////////////////////////////
1823
1824SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1825 : fMCRec(owner)
1826 , fCamera(camera)
1827{
1828 // assumes the mcrec has already been concatenated with the camera
1829 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1830 fInvPostCamera.setIdentity();
1831 }
1832}
1833
Mike Reed403c8072020-01-08 10:40:39 -05001834SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001835 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836}
1837
Mike Reed46f5c5f2020-02-20 15:42:29 -05001838SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001839 return fMCRec->fMatrix;
1840}
1841
Mike Reedc43f2a02020-01-16 14:54:34 -05001842SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001843 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001844 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001845 } else {
1846 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001847 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001848 }
1849}
1850
Mike Reedc43f2a02020-01-16 14:54:34 -05001851SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001852 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001853 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001854 } else {
1855 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001856 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001857 }
1858}
1859
Mike Reed46f5c5f2020-02-20 15:42:29 -05001860void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1861 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001862}
1863
1864void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1865 this->experimental_getLocalToWorld().getColMajor(colMajor);
1866}
1867
1868void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1869 this->experimental_getLocalToCamera().getColMajor(colMajor);
1870}
1871
Brian Osman11052242016-10-27 14:47:55 -04001872GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001873 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001874 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001875}
1876
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001877GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001878 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001879 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001880}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001881
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001882void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1883 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001884 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001885 if (outer.isEmpty()) {
1886 return;
1887 }
1888 if (inner.isEmpty()) {
1889 this->drawRRect(outer, paint);
1890 return;
1891 }
1892
1893 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001894 // be able to return ...
1895 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001896 //
1897 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001898 if (!outer.getBounds().contains(inner.getBounds())) {
1899 return;
1900 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001901
1902 this->onDrawDRRect(outer, inner, paint);
1903}
1904
reed41af9662015-01-05 07:49:08 -08001905void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001906 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001907 this->onDrawPaint(paint);
1908}
1909
1910void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001911 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001912 // To avoid redundant logic in our culling code and various backends, we always sort rects
1913 // before passing them along.
1914 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001915}
1916
Mike Reedd5674082019-04-19 15:00:47 -04001917void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1918 TRACE_EVENT0("skia", TRACE_FUNC);
1919 this->onDrawBehind(paint);
1920}
1921
msarettdca352e2016-08-26 06:37:45 -07001922void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001923 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001924 if (region.isEmpty()) {
1925 return;
1926 }
1927
1928 if (region.isRect()) {
1929 return this->drawIRect(region.getBounds(), paint);
1930 }
1931
1932 this->onDrawRegion(region, paint);
1933}
1934
reed41af9662015-01-05 07:49:08 -08001935void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001936 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001937 // To avoid redundant logic in our culling code and various backends, we always sort rects
1938 // before passing them along.
1939 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001940}
1941
1942void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001943 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001944 this->onDrawRRect(rrect, paint);
1945}
1946
1947void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001948 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001949 this->onDrawPoints(mode, count, pts, paint);
1950}
1951
Mike Reede88a1cb2017-03-17 09:50:46 -04001952void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1953 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001954 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001955 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001956 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1957 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001958 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001959}
1960
1961void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001962 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001963 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001964 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1965}
1966
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001967void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1968 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001969 TRACE_EVENT0("skia", TRACE_FUNC);
1970 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001971 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001972 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1973}
1974
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001975void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1976 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001977 TRACE_EVENT0("skia", TRACE_FUNC);
1978 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001979 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001980 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001981}
1982
1983void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001984 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001985 this->onDrawPath(path, paint);
1986}
1987
reeda85d4d02015-05-06 12:56:48 -07001988void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001989 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001990 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001991 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001992}
1993
Mike Reedc4e31092018-01-30 11:15:27 -05001994// Returns true if the rect can be "filled" : non-empty and finite
1995static bool fillable(const SkRect& r) {
1996 SkScalar w = r.width();
1997 SkScalar h = r.height();
1998 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1999}
2000
reede47829b2015-08-06 10:02:53 -07002001void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2002 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002003 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002004 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05002005 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07002006 return;
2007 }
2008 this->onDrawImageRect(image, &src, dst, paint, constraint);
2009}
reed41af9662015-01-05 07:49:08 -08002010
reed84984ef2015-07-17 07:09:43 -07002011void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2012 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002013 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002014 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002015}
2016
Brian Salomonf08002c2018-10-26 16:15:46 -04002017void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002018 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002019 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002020 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002021}
reede47829b2015-08-06 10:02:53 -07002022
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002023namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002024class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002025public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002026 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2027 if (!origPaint) {
2028 return;
2029 }
2030 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2031 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2032 }
2033 if (origPaint->getMaskFilter()) {
2034 fPaint.writable()->setMaskFilter(nullptr);
2035 }
2036 if (origPaint->isAntiAlias()) {
2037 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002038 }
2039 }
2040
2041 const SkPaint* get() const {
2042 return fPaint;
2043 }
2044
2045private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002046 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002047};
2048} // namespace
2049
reed4c21dc52015-06-25 12:32:03 -07002050void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2051 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002052 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002053 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002054 if (dst.isEmpty()) {
2055 return;
2056 }
msarett552bca92016-08-03 06:53:26 -07002057 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002058 LatticePaint latticePaint(paint);
2059 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002060 } else {
reede47829b2015-08-06 10:02:53 -07002061 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002062 }
reed4c21dc52015-06-25 12:32:03 -07002063}
2064
msarett16882062016-08-16 09:31:08 -07002065void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2066 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002067 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002068 RETURN_ON_NULL(image);
2069 if (dst.isEmpty()) {
2070 return;
2071 }
msarett71df2d72016-09-30 12:41:42 -07002072
2073 SkIRect bounds;
2074 Lattice latticePlusBounds = lattice;
2075 if (!latticePlusBounds.fBounds) {
2076 bounds = SkIRect::MakeWH(image->width(), image->height());
2077 latticePlusBounds.fBounds = &bounds;
2078 }
2079
2080 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002081 LatticePaint latticePaint(paint);
2082 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002083 } else {
2084 this->drawImageRect(image, dst, paint);
2085 }
2086}
2087
reed41af9662015-01-05 07:49:08 -08002088void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002089 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002090 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002091 return;
2092 }
reed41af9662015-01-05 07:49:08 -08002093 this->onDrawBitmap(bitmap, dx, dy, paint);
2094}
2095
reede47829b2015-08-06 10:02:53 -07002096void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002097 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002098 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002099 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002100 return;
2101 }
reede47829b2015-08-06 10:02:53 -07002102 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002103}
2104
reed84984ef2015-07-17 07:09:43 -07002105void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2106 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002107 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002108}
2109
reede47829b2015-08-06 10:02:53 -07002110void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2111 SrcRectConstraint constraint) {
2112 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2113 constraint);
2114}
reede47829b2015-08-06 10:02:53 -07002115
reed41af9662015-01-05 07:49:08 -08002116void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2117 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002118 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002119 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002120 return;
2121 }
msarett552bca92016-08-03 06:53:26 -07002122 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002123 LatticePaint latticePaint(paint);
2124 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002125 } else {
reeda5517e22015-07-14 10:54:12 -07002126 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002127 }
reed41af9662015-01-05 07:49:08 -08002128}
2129
msarettc573a402016-08-02 08:05:56 -07002130void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2131 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002132 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002133 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002134 return;
2135 }
msarett71df2d72016-09-30 12:41:42 -07002136
2137 SkIRect bounds;
2138 Lattice latticePlusBounds = lattice;
2139 if (!latticePlusBounds.fBounds) {
2140 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2141 latticePlusBounds.fBounds = &bounds;
2142 }
2143
2144 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002145 LatticePaint latticePaint(paint);
2146 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002147 } else {
msarett16882062016-08-16 09:31:08 -07002148 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002149 }
msarettc573a402016-08-02 08:05:56 -07002150}
2151
reed71c3c762015-06-24 10:29:17 -07002152void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002153 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002154 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002155 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002156 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002157 if (count <= 0) {
2158 return;
2159 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002160 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002161 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002162 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002163}
2164
reedf70b5312016-03-04 16:36:20 -08002165void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002166 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002167 if (key) {
2168 this->onDrawAnnotation(rect, key, value);
2169 }
2170}
2171
reede47829b2015-08-06 10:02:53 -07002172void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2173 const SkPaint* paint, SrcRectConstraint constraint) {
2174 if (src) {
2175 this->drawImageRect(image, *src, dst, paint, constraint);
2176 } else {
2177 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2178 dst, paint, constraint);
2179 }
2180}
2181void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2182 const SkPaint* paint, SrcRectConstraint constraint) {
2183 if (src) {
2184 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2185 } else {
2186 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2187 dst, paint, constraint);
2188 }
2189}
2190
Mike Reed4204da22017-05-17 08:53:36 -04002191void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002192 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002193 this->onDrawShadowRec(path, rec);
2194}
2195
2196void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2197 SkPaint paint;
2198 const SkRect& pathBounds = path.getBounds();
2199
Mike Reed38992392019-07-30 10:48:15 -04002200 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002201 while (iter.next()) {
2202 iter.fDevice->drawShadow(path, rec);
2203 }
Mike Reed38992392019-07-30 10:48:15 -04002204 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002205}
2206
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002207void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002208 QuadAAFlags aaFlags, const SkColor4f& color,
2209 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002210 TRACE_EVENT0("skia", TRACE_FUNC);
2211 // Make sure the rect is sorted before passing it along
2212 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2213}
2214
2215void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2216 const SkPoint dstClips[],
2217 const SkMatrix preViewMatrices[],
2218 const SkPaint* paint,
2219 SrcRectConstraint constraint) {
2220 TRACE_EVENT0("skia", TRACE_FUNC);
2221 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2222}
2223
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224//////////////////////////////////////////////////////////////////////////////
2225// These are the virtual drawing methods
2226//////////////////////////////////////////////////////////////////////////////
2227
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002228void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002229 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002230 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2231 }
2232}
2233
reed41af9662015-01-05 07:49:08 -08002234void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002235 this->internalDrawPaint(paint);
2236}
2237
2238void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002239 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240
2241 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002242 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243 }
2244
Mike Reed38992392019-07-30 10:48:15 -04002245 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246}
2247
reed41af9662015-01-05 07:49:08 -08002248void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2249 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 if ((long)count <= 0) {
2251 return;
2252 }
2253
Mike Reed822128b2017-02-28 16:41:03 -05002254 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002255 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002256 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002257 // special-case 2 points (common for drawing a single line)
2258 if (2 == count) {
2259 r.set(pts[0], pts[1]);
2260 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002261 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002262 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002263 if (!r.isFinite()) {
2264 return;
2265 }
Mike Reed822128b2017-02-28 16:41:03 -05002266 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002267 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2268 return;
2269 }
2270 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002271 }
reed@google.coma584aed2012-05-16 14:06:02 +00002272
halcanary96fcdcc2015-08-27 07:41:13 -07002273 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274
Mike Reed38992392019-07-30 10:48:15 -04002275 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002276
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002278 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 }
reed@google.com4b226022011-01-11 18:32:13 +00002280
Mike Reed38992392019-07-30 10:48:15 -04002281 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282}
2283
reed4a167172016-08-18 17:15:25 -07002284static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002285 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002286}
2287
reed41af9662015-01-05 07:49:08 -08002288void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002289 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002291 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002292 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002293 return;
2294 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 }
reed@google.com4b226022011-01-11 18:32:13 +00002296
reed4a167172016-08-18 17:15:25 -07002297 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002298 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002299
reed4a167172016-08-18 17:15:25 -07002300 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002301 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002302 }
2303
Mike Reed38992392019-07-30 10:48:15 -04002304 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002305 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002306 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002307 SkDrawIter iter(this);
2308 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002309 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312}
2313
msarett44df6512016-08-25 13:54:30 -07002314void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002315 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002316 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002317 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002318 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2319 return;
2320 }
msarett44df6512016-08-25 13:54:30 -07002321 }
2322
Mike Reed38992392019-07-30 10:48:15 -04002323 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002324
2325 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002326 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002327 }
2328
Mike Reed38992392019-07-30 10:48:15 -04002329 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002330}
2331
Mike Reedd5674082019-04-19 15:00:47 -04002332void SkCanvas::onDrawBehind(const SkPaint& paint) {
2333 SkIRect bounds;
2334 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2335 for (;;) {
2336 const MCRec* rec = (const MCRec*)iter.prev();
2337 if (!rec) {
2338 return; // no backimages, so nothing to draw
2339 }
2340 if (rec->fBackImage) {
2341 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2342 rec->fBackImage->fImage->width(),
2343 rec->fBackImage->fImage->height());
2344 break;
2345 }
2346 }
2347
Mike Reed38992392019-07-30 10:48:15 -04002348 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002349
2350 while (iter.next()) {
2351 SkBaseDevice* dev = iter.fDevice;
2352
Mike Reedd5674082019-04-19 15:00:47 -04002353 dev->save();
2354 // We use clipRegion because it is already defined to operate in dev-space
2355 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2356 // but we don't want that, so we undo that before calling in.
Michael Ludwigfb3f3022020-02-20 13:23:58 -05002357 SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
Mike Reedd5674082019-04-19 15:00:47 -04002358 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002359 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002360 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002361 }
2362
Mike Reed38992392019-07-30 10:48:15 -04002363 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002364}
2365
reed41af9662015-01-05 07:49:08 -08002366void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002367 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002368 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002369 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002370 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002371 return;
2372 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002373 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002374
Mike Reed38992392019-07-30 10:48:15 -04002375 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002376
2377 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002378 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002379 }
2380
Mike Reed38992392019-07-30 10:48:15 -04002381 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002382}
2383
bsalomonac3aa242016-08-19 11:25:19 -07002384void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2385 SkScalar sweepAngle, bool useCenter,
2386 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002387 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002388 if (paint.canComputeFastBounds()) {
2389 SkRect storage;
2390 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002391 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002392 return;
2393 }
bsalomonac3aa242016-08-19 11:25:19 -07002394 }
2395
Mike Reed38992392019-07-30 10:48:15 -04002396 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002397
2398 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002399 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002400 }
2401
Mike Reed38992392019-07-30 10:48:15 -04002402 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002403}
2404
reed41af9662015-01-05 07:49:08 -08002405void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002406 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002407 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002408 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2409 return;
2410 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002411 }
2412
2413 if (rrect.isRect()) {
2414 // call the non-virtual version
2415 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002416 return;
2417 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002418 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002419 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2420 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002421 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002422
Mike Reed38992392019-07-30 10:48:15 -04002423 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002424
2425 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002426 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002427 }
2428
Mike Reed38992392019-07-30 10:48:15 -04002429 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002430}
2431
Mike Reed822128b2017-02-28 16:41:03 -05002432void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002433 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002434 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002435 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2436 return;
2437 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002438 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002439
Mike Reed38992392019-07-30 10:48:15 -04002440 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002441
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002442 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002443 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002444 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002445
Mike Reed38992392019-07-30 10:48:15 -04002446 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002447}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002448
reed41af9662015-01-05 07:49:08 -08002449void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002450 if (!path.isFinite()) {
2451 return;
2452 }
2453
Mike Reed822128b2017-02-28 16:41:03 -05002454 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002455 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002456 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002457 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2458 return;
2459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002461
Mike Reed822128b2017-02-28 16:41:03 -05002462 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002463 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002464 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002465 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002466 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002467 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468
Mike Reed38992392019-07-30 10:48:15 -04002469 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002470
2471 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002472 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002473 }
2474
Mike Reed38992392019-07-30 10:48:15 -04002475 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002476}
2477
reed262a71b2015-12-05 13:07:27 -08002478bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002479 if (!paint.getImageFilter()) {
2480 return false;
2481 }
2482
2483 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002484 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002485 return false;
2486 }
2487
2488 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2489 // Once we can filter and the filter will return a result larger than itself, we should be
2490 // able to remove this constraint.
2491 // skbug.com/4526
2492 //
2493 SkPoint pt;
2494 ctm.mapXY(x, y, &pt);
2495 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2496 return ir.contains(fMCRec->fRasterClip.getBounds());
2497}
2498
Mike Reedf441cfc2018-04-11 14:50:16 -04002499// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2500// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2501// null.
2502static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2503 if (paintParam) {
2504 *real = *paintParam;
2505 real->setStyle(SkPaint::kFill_Style);
2506 real->setPathEffect(nullptr);
2507 paintParam = real;
2508 }
2509 return paintParam;
2510}
2511
reeda85d4d02015-05-06 12:56:48 -07002512void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002513 SkPaint realPaint;
2514 paint = init_image_paint(&realPaint, paint);
2515
reeda85d4d02015-05-06 12:56:48 -07002516 SkRect bounds = SkRect::MakeXYWH(x, y,
2517 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002518 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002519 SkRect tmp = bounds;
2520 if (paint) {
2521 paint->computeFastBounds(tmp, &tmp);
2522 }
2523 if (this->quickReject(tmp)) {
2524 return;
2525 }
reeda85d4d02015-05-06 12:56:48 -07002526 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002527 // At this point we need a real paint object. If the caller passed null, then we should
2528 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2529 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2530 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002531
reeda2217ef2016-07-20 06:04:34 -07002532 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002533 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2534 *paint);
2535 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002536 special = this->getDevice()->makeSpecial(image);
2537 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002538 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002539 }
2540 }
2541
Mike Reed38992392019-07-30 10:48:15 -04002542 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002543
reeda85d4d02015-05-06 12:56:48 -07002544 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002545 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002546 if (special) {
2547 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002548 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002549 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002550 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002551 SkScalarRoundToInt(pt.fY), pnt,
2552 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002553 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002554 iter.fDevice->drawImageRect(
2555 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2556 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002557 }
reeda85d4d02015-05-06 12:56:48 -07002558 }
halcanary9d524f22016-03-29 09:03:52 -07002559
Mike Reed38992392019-07-30 10:48:15 -04002560 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002561}
2562
reed41af9662015-01-05 07:49:08 -08002563void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002564 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002565 SkPaint realPaint;
2566 paint = init_image_paint(&realPaint, paint);
2567
halcanary96fcdcc2015-08-27 07:41:13 -07002568 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002569 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002570 if (paint) {
2571 paint->computeFastBounds(dst, &storage);
2572 }
2573 if (this->quickReject(storage)) {
2574 return;
2575 }
reeda85d4d02015-05-06 12:56:48 -07002576 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002577 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002578
Mike Reed38992392019-07-30 10:48:15 -04002579 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002580
reeda85d4d02015-05-06 12:56:48 -07002581 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002582 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002583 }
halcanary9d524f22016-03-29 09:03:52 -07002584
Mike Reed38992392019-07-30 10:48:15 -04002585 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002586}
2587
reed41af9662015-01-05 07:49:08 -08002588void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002589 SkDEBUGCODE(bitmap.validate();)
2590
reed33366972015-10-08 09:22:02 -07002591 if (bitmap.drawsNothing()) {
2592 return;
2593 }
2594
Mike Reedf441cfc2018-04-11 14:50:16 -04002595 SkPaint realPaint;
2596 init_image_paint(&realPaint, paint);
2597 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002598
Mike Reed822128b2017-02-28 16:41:03 -05002599 SkRect bounds;
2600 bitmap.getBounds(&bounds);
2601 bounds.offset(x, y);
2602 bool canFastBounds = paint->canComputeFastBounds();
2603 if (canFastBounds) {
2604 SkRect storage;
2605 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002606 return;
2607 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002608 }
reed@google.com4b226022011-01-11 18:32:13 +00002609
reeda2217ef2016-07-20 06:04:34 -07002610 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002611 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2612 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002613 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002614 special = this->getDevice()->makeSpecial(bitmap);
2615 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002616 drawAsSprite = false;
2617 }
2618 }
2619
Mike Reed38992392019-07-30 10:48:15 -04002620 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002621
2622 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002623 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002624 if (special) {
reed262a71b2015-12-05 13:07:27 -08002625 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002626 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002627 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002628 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002629 SkScalarRoundToInt(pt.fY), pnt,
2630 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002631 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002632 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2633 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2634 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002635 }
reed33366972015-10-08 09:22:02 -07002636 }
msarettfbfa2582016-08-12 08:29:08 -07002637
Mike Reed38992392019-07-30 10:48:15 -04002638 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002639}
2640
reed@google.com9987ec32011-09-07 11:57:52 +00002641// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002642void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002643 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002644 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002645 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002646 return;
2647 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002648
halcanary96fcdcc2015-08-27 07:41:13 -07002649 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002650 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002651 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2652 return;
2653 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002654 }
reed@google.com3d608122011-11-21 15:16:16 +00002655
reed@google.com33535f32012-09-25 15:37:50 +00002656 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002657 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002658 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002659 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002660
Mike Reed38992392019-07-30 10:48:15 -04002661 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002662
reed@google.com33535f32012-09-25 15:37:50 +00002663 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002664 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002665 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002666
Mike Reed38992392019-07-30 10:48:15 -04002667 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002668}
2669
reed41af9662015-01-05 07:49:08 -08002670void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002671 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002672 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002673 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002674}
2675
reed4c21dc52015-06-25 12:32:03 -07002676void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2677 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002678 SkPaint realPaint;
2679 paint = init_image_paint(&realPaint, paint);
2680
halcanary96fcdcc2015-08-27 07:41:13 -07002681 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002682 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002683 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2684 return;
2685 }
reed@google.com3d608122011-11-21 15:16:16 +00002686 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002687 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002688
Mike Reed38992392019-07-30 10:48:15 -04002689 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002690
reed4c21dc52015-06-25 12:32:03 -07002691 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002692 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002693 }
halcanary9d524f22016-03-29 09:03:52 -07002694
Mike Reed38992392019-07-30 10:48:15 -04002695 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002696}
2697
reed41af9662015-01-05 07:49:08 -08002698void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2699 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002700 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002701 SkPaint realPaint;
2702 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002703
halcanary96fcdcc2015-08-27 07:41:13 -07002704 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002705 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002706 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2707 return;
2708 }
reed4c21dc52015-06-25 12:32:03 -07002709 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002710 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002711
Mike Reed38992392019-07-30 10:48:15 -04002712 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002713
reed4c21dc52015-06-25 12:32:03 -07002714 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002715 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002716 }
halcanary9d524f22016-03-29 09:03:52 -07002717
Mike Reed38992392019-07-30 10:48:15 -04002718 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002719}
2720
msarett16882062016-08-16 09:31:08 -07002721void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2722 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002723 SkPaint realPaint;
2724 paint = init_image_paint(&realPaint, paint);
2725
msarett16882062016-08-16 09:31:08 -07002726 if (nullptr == paint || paint->canComputeFastBounds()) {
2727 SkRect storage;
2728 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2729 return;
2730 }
2731 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002732 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002733
Mike Reed38992392019-07-30 10:48:15 -04002734 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002735
2736 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002737 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002738 }
2739
Mike Reed38992392019-07-30 10:48:15 -04002740 DRAW_END
msarett16882062016-08-16 09:31:08 -07002741}
2742
2743void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2744 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002745 SkPaint realPaint;
2746 paint = init_image_paint(&realPaint, paint);
2747
msarett16882062016-08-16 09:31:08 -07002748 if (nullptr == paint || paint->canComputeFastBounds()) {
2749 SkRect storage;
2750 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2751 return;
2752 }
2753 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002754 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002755
Mike Reed38992392019-07-30 10:48:15 -04002756 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002757
2758 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002759 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002760 }
2761
Mike Reed38992392019-07-30 10:48:15 -04002762 DRAW_END
msarett16882062016-08-16 09:31:08 -07002763}
2764
fmalita00d5c2c2014-08-21 08:53:26 -07002765void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2766 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002767 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002768 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002769 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002770 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002771 SkRect tmp;
2772 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2773 return;
2774 }
2775 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002776 }
2777
fmalita024f9962015-03-03 19:08:17 -08002778 // We cannot filter in the looper as we normally do, because the paint is
2779 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002780 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002781
fmalitaaa1b9122014-08-28 14:32:24 -07002782 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002783 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002784 }
2785
Mike Reed38992392019-07-30 10:48:15 -04002786 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002787}
2788
Mike Reed358fcad2018-11-23 15:27:51 -05002789// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002790void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002791 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2792 TRACE_EVENT0("skia", TRACE_FUNC);
2793 if (byteLength) {
2794 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002795 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002796 }
2797}
Mike Reed4f81bb72019-01-23 09:23:00 -05002798
fmalita00d5c2c2014-08-21 08:53:26 -07002799void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2800 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002801 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002802 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002803 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002804 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002805}
reed@google.come0d9ce82014-04-23 04:00:17 +00002806
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002807void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002808 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002809 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002810
2811 while (iter.next()) {
2812 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002813 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002814 }
2815
Mike Reed38992392019-07-30 10:48:15 -04002816 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002817}
2818
dandovb3c9d1c2014-08-12 08:34:29 -07002819void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002820 const SkPoint texCoords[4], SkBlendMode bmode,
2821 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002822 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002823 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002824 return;
2825 }
mtklein6cfa73a2014-08-13 13:33:49 -07002826
Mike Reedfaba3712016-11-03 14:45:31 -04002827 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002828}
2829
2830void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002831 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002832 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002833 // Since a patch is always within the convex hull of the control points, we discard it when its
2834 // bounding rectangle is completely outside the current clip.
2835 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002836 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002837 if (this->quickReject(bounds)) {
2838 return;
2839 }
mtklein6cfa73a2014-08-13 13:33:49 -07002840
Mike Reed38992392019-07-30 10:48:15 -04002841 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002842
dandovecfff212014-08-04 10:02:00 -07002843 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002844 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002845 }
mtklein6cfa73a2014-08-13 13:33:49 -07002846
Mike Reed38992392019-07-30 10:48:15 -04002847 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002848}
2849
reeda8db7282015-07-07 10:22:31 -07002850void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002851#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002852 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002853#endif
reede3b38ce2016-01-08 09:18:44 -08002854 RETURN_ON_NULL(dr);
2855 if (x || y) {
2856 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2857 this->onDrawDrawable(dr, &matrix);
2858 } else {
2859 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002860 }
2861}
2862
reeda8db7282015-07-07 10:22:31 -07002863void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002864#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002865 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002866#endif
reede3b38ce2016-01-08 09:18:44 -08002867 RETURN_ON_NULL(dr);
2868 if (matrix && matrix->isIdentity()) {
2869 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002870 }
reede3b38ce2016-01-08 09:18:44 -08002871 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002872}
2873
2874void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002875 // drawable bounds are no longer reliable (e.g. android displaylist)
2876 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002877 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002878}
2879
reed71c3c762015-06-24 10:29:17 -07002880void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002881 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002882 const SkRect* cull, const SkPaint* paint) {
2883 if (cull && this->quickReject(*cull)) {
2884 return;
2885 }
2886
2887 SkPaint pnt;
2888 if (paint) {
2889 pnt = *paint;
2890 }
halcanary9d524f22016-03-29 09:03:52 -07002891
Mike Reed38992392019-07-30 10:48:15 -04002892 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002893 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002894 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002895 }
Mike Reed38992392019-07-30 10:48:15 -04002896 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002897}
2898
reedf70b5312016-03-04 16:36:20 -08002899void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2900 SkASSERT(key);
2901
2902 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002903 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002904 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002905 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002906 }
Mike Reed38992392019-07-30 10:48:15 -04002907 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002908}
2909
Michael Ludwiga595f862019-08-27 15:25:49 -04002910void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2911 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002912 SkASSERT(r.isSorted());
2913
2914 // If this used a paint, it would be a filled color with blend mode, which does not
2915 // need to use an autodraw loop, so use SkDrawIter directly.
2916 if (this->quickReject(r)) {
2917 return;
2918 }
2919
Michael Ludwiga4b44882019-08-28 14:34:58 -04002920 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002921 SkDrawIter iter(this);
2922 while(iter.next()) {
2923 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2924 }
2925}
2926
2927void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2928 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2929 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002930 if (count <= 0) {
2931 // Nothing to draw
2932 return;
2933 }
2934
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002935 SkPaint realPaint;
2936 init_image_paint(&realPaint, paint);
2937
Michael Ludwiga4b44882019-08-28 14:34:58 -04002938 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2939 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2940 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2941 // or we need it for the autolooper (since it greatly improves image filter perf).
2942 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2943 bool setBoundsValid = count == 1 || needsAutoLooper;
2944 SkRect setBounds = imageSet[0].fDstRect;
2945 if (imageSet[0].fMatrixIndex >= 0) {
2946 // Account for the per-entry transform that is applied prior to the CTM when drawing
2947 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002948 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002949 if (needsAutoLooper) {
2950 for (int i = 1; i < count; ++i) {
2951 SkRect entryBounds = imageSet[i].fDstRect;
2952 if (imageSet[i].fMatrixIndex >= 0) {
2953 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2954 }
2955 setBounds.joinPossiblyEmptyRect(entryBounds);
2956 }
2957 }
2958
2959 // If we happen to have the draw bounds, though, might as well check quickReject().
2960 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2961 SkRect tmp;
2962 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2963 return;
2964 }
2965 }
2966
2967 if (needsAutoLooper) {
2968 SkASSERT(setBoundsValid);
2969 DRAW_BEGIN(realPaint, &setBounds)
2970 while (iter.next()) {
2971 iter.fDevice->drawEdgeAAImageSet(
2972 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2973 }
2974 DRAW_END
2975 } else {
2976 this->predrawNotify();
2977 SkDrawIter iter(this);
2978 while(iter.next()) {
2979 iter.fDevice->drawEdgeAAImageSet(
2980 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2981 }
2982 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002983}
2984
reed@android.com8a1c16f2008-12-17 15:59:43 +00002985//////////////////////////////////////////////////////////////////////////////
2986// These methods are NOT virtual, and therefore must call back into virtual
2987// methods, rather than actually drawing themselves.
2988//////////////////////////////////////////////////////////////////////////////
2989
reed374772b2016-10-05 17:33:02 -07002990void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002991 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002992 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002993 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002994 this->drawPaint(paint);
2995}
2996
2997void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002998 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002999 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3000}
3001
Mike Reed3661bc92017-02-22 13:21:42 -05003002void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003003 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00003004 pts[0].set(x0, y0);
3005 pts[1].set(x1, y1);
3006 this->drawPoints(kLines_PointMode, 2, pts, paint);
3007}
3008
Mike Reed3661bc92017-02-22 13:21:42 -05003009void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003010 if (radius < 0) {
3011 radius = 0;
3012 }
3013
3014 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04003015 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003016 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017}
3018
3019void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3020 const SkPaint& paint) {
3021 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003022 SkRRect rrect;
3023 rrect.setRectXY(r, rx, ry);
3024 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003025 } else {
3026 this->drawRect(r, paint);
3027 }
3028}
3029
reed@android.com8a1c16f2008-12-17 15:59:43 +00003030void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3031 SkScalar sweepAngle, bool useCenter,
3032 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04003033 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07003034 if (oval.isEmpty() || !sweepAngle) {
3035 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003036 }
bsalomon21af9ca2016-08-25 12:29:23 -07003037 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003038}
3039
reed@android.comf76bacf2009-05-13 14:00:33 +00003040///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003041#ifdef SK_DISABLE_SKPICTURE
3042void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07003043
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003044
3045void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3046 const SkPaint* paint) {}
3047#else
Mike Klein88d90712018-01-27 17:30:04 +00003048/**
3049 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3050 * against the playback cost of recursing into the subpicture to get at its actual ops.
3051 *
3052 * For now we pick a conservatively small value, though measurement (and other heuristics like
3053 * the type of ops contained) may justify changing this value.
3054 */
3055#define kMaxPictureOpsToUnrollInsteadOfRef 1
3056
reedd5fa1a42014-08-09 11:08:05 -07003057void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04003058 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08003059 RETURN_ON_NULL(picture);
3060
reede3b38ce2016-01-08 09:18:44 -08003061 if (matrix && matrix->isIdentity()) {
3062 matrix = nullptr;
3063 }
Mike Klein88d90712018-01-27 17:30:04 +00003064 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3065 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3066 picture->playback(this);
3067 } else {
3068 this->onDrawPicture(picture, matrix, paint);
3069 }
reedd5fa1a42014-08-09 11:08:05 -07003070}
robertphillips9b14f262014-06-04 05:40:44 -07003071
reedd5fa1a42014-08-09 11:08:05 -07003072void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3073 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003074 if (!paint || paint->canComputeFastBounds()) {
3075 SkRect bounds = picture->cullRect();
3076 if (paint) {
3077 paint->computeFastBounds(bounds, &bounds);
3078 }
3079 if (matrix) {
3080 matrix->mapRect(&bounds);
3081 }
3082 if (this->quickReject(bounds)) {
3083 return;
3084 }
3085 }
3086
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003087 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003088 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003089}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003090#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003091
reed@android.com8a1c16f2008-12-17 15:59:43 +00003092///////////////////////////////////////////////////////////////////////////////
3093///////////////////////////////////////////////////////////////////////////////
3094
reed3aafe112016-08-18 12:45:34 -07003095SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003096 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003097
3098 SkASSERT(canvas);
3099
reed3aafe112016-08-18 12:45:34 -07003100 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003101 fDone = !fImpl->next();
3102}
3103
3104SkCanvas::LayerIter::~LayerIter() {
3105 fImpl->~SkDrawIter();
3106}
3107
3108void SkCanvas::LayerIter::next() {
3109 fDone = !fImpl->next();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003110 if (!fDone) {
3111 // Cache the device origin. LayerIter is only used in Android, which doesn't use image
3112 // filters, so its devices will always be able to report the origin exactly.
3113 fDeviceOrigin = fImpl->fDevice->getOrigin();
3114 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003115}
3116
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003117SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003118 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003119}
3120
3121const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003122 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003123}
3124
3125const SkPaint& SkCanvas::LayerIter::paint() const {
3126 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003127 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003128 paint = &fDefaultPaint;
3129 }
3130 return *paint;
3131}
3132
Mike Reedca37f322018-03-08 13:22:16 -05003133SkIRect SkCanvas::LayerIter::clipBounds() const {
3134 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003135}
3136
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003137int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; }
3138int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003139
3140///////////////////////////////////////////////////////////////////////////////
3141
Brian Osmane8a98632019-04-10 10:26:10 -04003142SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3143SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3144SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3145SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3146
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003147SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3148 const SkRect& dstRect, int matrixIndex, float alpha,
3149 unsigned aaFlags, bool hasClip)
3150 : fImage(std::move(image))
3151 , fSrcRect(srcRect)
3152 , fDstRect(dstRect)
3153 , fMatrixIndex(matrixIndex)
3154 , fAlpha(alpha)
3155 , fAAFlags(aaFlags)
3156 , fHasClip(hasClip) {}
3157
3158SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3159 const SkRect& dstRect, float alpha, unsigned aaFlags)
3160 : fImage(std::move(image))
3161 , fSrcRect(srcRect)
3162 , fDstRect(dstRect)
3163 , fAlpha(alpha)
3164 , fAAFlags(aaFlags) {}
3165
3166///////////////////////////////////////////////////////////////////////////////
3167
Mike Reed5df49342016-11-12 08:06:55 -06003168std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003169 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003170 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003171 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003172 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003173
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003174 SkBitmap bitmap;
3175 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003176 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003177 }
Mike Reed12f77342017-11-08 11:19:52 -05003178
3179 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003180 std::make_unique<SkCanvas>(bitmap, *props) :
3181 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003182}
reedd5fa1a42014-08-09 11:08:05 -07003183
3184///////////////////////////////////////////////////////////////////////////////
3185
Florin Malitaee424ac2016-12-01 12:47:59 -05003186SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003187 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003188
Florin Malita439ace92016-12-02 12:05:41 -05003189SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003190 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003191
Herb Derbyefe39bc2018-05-01 17:06:20 -04003192SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003193 : INHERITED(device) {}
3194
Florin Malitaee424ac2016-12-01 12:47:59 -05003195SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3196 (void)this->INHERITED::getSaveLayerStrategy(rec);
3197 return kNoLayer_SaveLayerStrategy;
3198}
3199
Mike Reed148b7fd2018-12-18 17:38:18 -05003200bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3201 return false;
3202}
3203
Florin Malitaee424ac2016-12-01 12:47:59 -05003204///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003205
reed73603f32016-09-20 08:42:38 -07003206static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3207static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3208static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3209static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3210static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3211static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003212
3213///////////////////////////////////////////////////////////////////////////////////////////////////
3214
3215SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3216 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003217 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003218 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003219 SkIRect clip = dev->devClipBounds();
Mike Reed92b33352019-08-24 19:39:13 -04003220 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003221 clip.setEmpty();
3222 }
3223
Michael Ludwigfb3f3022020-02-20 13:23:58 -05003224 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003225 return handle;
3226 }
3227 return nullptr;
3228}
3229
3230static bool install(SkBitmap* bm, const SkImageInfo& info,
3231 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003232 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003233}
3234
3235SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3236 SkBitmap* bm) {
3237 SkRasterHandleAllocator::Rec rec;
3238 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3239 return nullptr;
3240 }
3241 return rec.fHandle;
3242}
3243
3244std::unique_ptr<SkCanvas>
3245SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3246 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003247 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003248 return nullptr;
3249 }
3250
3251 SkBitmap bm;
3252 Handle hndl;
3253
3254 if (rec) {
3255 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3256 } else {
3257 hndl = alloc->allocBitmap(info, &bm);
3258 }
3259 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3260}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003261
3262///////////////////////////////////////////////////////////////////////////////////////////////////