blob: 6e4d5346f12d7ae58aeabe58fb386d76afc0c4b6 [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
Michael Ludwig915b7792019-10-22 17:40:41 +0000293 int getX() const { return fDevice->getOrigin().x(); }
294 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000296
Mike Reed99330ba2017-02-22 11:01:08 -0500297 SkBaseDevice* fDevice;
298
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 const DeviceCM* fCurrLayer;
301 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302};
303
Florin Malita713b8ef2017-04-28 10:57:24 -0400304#define FOR_EACH_TOP_DEVICE( code ) \
305 do { \
306 DeviceCM* layer = fMCRec->fTopLayer; \
307 while (layer) { \
308 SkBaseDevice* device = layer->fDevice.get(); \
309 if (device) { \
310 code; \
311 } \
312 layer = layer->fNext; \
313 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500314 } while (0)
315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316/////////////////////////////////////////////////////////////////////////////
317
reeddbc3cef2015-04-29 12:18:57 -0700318/**
319 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700320 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700321 */
reedd053ce92016-03-22 10:17:23 -0700322static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700323 SkImageFilter* imgf = paint.getImageFilter();
324 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700325 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700326 }
327
reedd053ce92016-03-22 10:17:23 -0700328 SkColorFilter* imgCFPtr;
329 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700330 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700331 }
reedd053ce92016-03-22 10:17:23 -0700332 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700333
334 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700335 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700336 // there is no existing paint colorfilter, so we can just return the imagefilter's
337 return imgCF;
338 }
339
340 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
341 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500342 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700343}
344
senorblanco87e066e2015-10-28 11:23:36 -0700345/**
346 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
347 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
348 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
349 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
350 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
351 * conservative "effective" bounds based on the settings in the paint... with one exception. This
352 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
353 * deliberately ignored.
354 */
355static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
356 const SkRect& rawBounds,
357 SkRect* storage) {
358 SkPaint tmpUnfiltered(paint);
359 tmpUnfiltered.setImageFilter(nullptr);
360 if (tmpUnfiltered.canComputeFastBounds()) {
361 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
362 } else {
363 return rawBounds;
364 }
365}
366
Mike Reed38992392019-07-30 10:48:15 -0400367class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368public:
senorblanco87e066e2015-10-28 11:23:36 -0700369 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
370 // paint. It's used to determine the size of the offscreen layer for filters.
371 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400372 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
373 bool skipLayerForImageFilter = false,
374 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400376 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000377 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700378 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
Mike Reed38992392019-07-30 10:48:15 -0400380 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
381 SkASSERT(!fLazyPaint.isValid());
382 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700383 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700384 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700385 fPaint = paint;
386 }
387
388 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700389 /**
390 * We implement ImageFilters for a given draw by creating a layer, then applying the
391 * imagefilter to the pixels of that layer (its backing surface/image), and then
392 * we call restore() to xfer that layer to the main canvas.
393 *
394 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
395 * 2. Generate the src pixels:
396 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
397 * return (fPaint). We then draw the primitive (using srcover) into a cleared
398 * buffer/surface.
399 * 3. Restore the layer created in #1
400 * The imagefilter is passed the buffer/surface from the layer (now filled with the
401 * src pixels of the primitive). It returns a new "filtered" buffer, which we
402 * draw onto the previous layer using the xfermode from the original paint.
403 */
Mike Reed38992392019-07-30 10:48:15 -0400404
405 SkPaint restorePaint;
406 restorePaint.setImageFilter(fPaint->refImageFilter());
407 restorePaint.setBlendMode(fPaint->getBlendMode());
408
senorblanco87e066e2015-10-28 11:23:36 -0700409 SkRect storage;
410 if (rawBounds) {
411 // Make rawBounds include all paint outsets except for those due to image filters.
412 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
413 }
Mike Reed38992392019-07-30 10:48:15 -0400414 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700415 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700416 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000417
Mike Reed38992392019-07-30 10:48:15 -0400418 // Remove the restorePaint fields from our "working" paint
419 SkASSERT(!fLazyPaint.isValid());
420 SkPaint* paint = fLazyPaint.set(origPaint);
421 paint->setImageFilter(nullptr);
422 paint->setBlendMode(SkBlendMode::kSrcOver);
423 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000424 }
425 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000426
Mike Reed38992392019-07-30 10:48:15 -0400427 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700428 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000429 fCanvas->internalRestore();
430 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000431 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000433
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434 const SkPaint& paint() const {
435 SkASSERT(fPaint);
436 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439private:
Mike Reed38992392019-07-30 10:48:15 -0400440 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400442 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000443 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700444 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445};
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447////////// macros to place around the internal draw calls //////////////////
448
Mike Reed38992392019-07-30 10:48:15 -0400449#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700450 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400451 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
452 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800453
454
Mike Reed38992392019-07-30 10:48:15 -0400455#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000456 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400457 AutoLayerForImageFilter draw(this, paint, true); \
458 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000459
Mike Reed38992392019-07-30 10:48:15 -0400460#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000461 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400462 AutoLayerForImageFilter draw(this, paint, false, bounds); \
463 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000464
Mike Reed38992392019-07-30 10:48:15 -0400465#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700466 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400467 AutoLayerForImageFilter draw(this, paint, false, bounds); \
468 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700469
Mike Reed38992392019-07-30 10:48:15 -0400470#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471
472////////////////////////////////////////////////////////////////////////////
473
msarettfbfa2582016-08-12 08:29:08 -0700474static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
475 if (bounds.isEmpty()) {
476 return SkRect::MakeEmpty();
477 }
478
479 // Expand bounds out by 1 in case we are anti-aliasing. We store the
480 // bounds as floats to enable a faster quick reject implementation.
481 SkRect dst;
482 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
483 return dst;
484}
485
mtkleinfeaadee2015-04-08 11:25:48 -0700486void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
487 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700488 fMCRec->reset(bounds);
489
490 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500491 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400492 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700493 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700494 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700495}
496
Hal Canary363a3f82018-10-04 11:04:48 -0400497void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800498 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500501 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500502 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700503 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reeda499f902015-05-01 09:34:31 -0700505 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
506 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400507 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510
halcanary96fcdcc2015-08-27 07:41:13 -0700511 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000512
reedf92c8662014-08-18 08:02:43 -0700513 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700514 // The root device and the canvas should always have the same pixel geometry
515 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800516 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700517 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518
Mike Reedc42a1cd2017-02-14 14:25:14 -0500519 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700520 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400521
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500522 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
reed@google.comcde92112011-07-06 20:00:52 +0000525SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700527 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000528{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000529 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000530
Hal Canary363a3f82018-10-04 11:04:48 -0400531 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000532}
533
reed96a857e2015-01-25 10:33:58 -0800534SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800536 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537{
538 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400539 this->init(sk_make_sp<SkNoPixelsDevice>(
Brian Osman788b9162020-02-07 10:36:46 -0500540 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700541}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000542
Hal Canary363a3f82018-10-04 11:04:48 -0400543SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700544 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700545 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700546{
547 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700548
Mike Reed566e53c2017-03-10 10:49:45 -0500549 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400550 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700551}
552
Herb Derbyefe39bc2018-05-01 17:06:20 -0400553SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700555 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000556{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700558
Hal Canary363a3f82018-10-04 11:04:48 -0400559 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700560}
561
reed4a8126e2014-09-22 07:29:03 -0700562SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700563 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700564 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700565{
566 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700567
Mike Reed910ca0f2018-04-25 13:04:05 -0400568 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400569 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700570}
reed29c857d2014-09-21 10:25:07 -0700571
Mike Reed356f7c22017-01-10 11:58:39 -0500572SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
573 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700574 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
575 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500576 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700577{
578 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700579
Mike Reed910ca0f2018-04-25 13:04:05 -0400580 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400581 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582}
583
Mike Reed356f7c22017-01-10 11:58:39 -0500584SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
585
Matt Sarett31f99ce2017-04-11 08:46:01 -0400586#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
587SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
588 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
589 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
590 , fAllocator(nullptr)
591{
592 inc_canvas();
593
594 SkBitmap tmp(bitmap);
595 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400596 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400597 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400598}
599#endif
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601SkCanvas::~SkCanvas() {
602 // free up the contents of our deque
603 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 this->internalRestore(); // restore the last, since we're going away
606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 dec_canvas();
608}
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610///////////////////////////////////////////////////////////////////////////////
611
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000612void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700613 this->onFlush();
614}
615
616void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000617 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000618 if (device) {
619 device->flush();
620 }
621}
622
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500623SkSurface* SkCanvas::getSurface() const {
624 return fSurfaceBase;
625}
626
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000627SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000628 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000629 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
630}
631
senorblancoafc7cce2016-02-02 18:44:15 -0800632SkIRect SkCanvas::getTopLayerBounds() const {
633 SkBaseDevice* d = this->getTopDevice();
634 if (!d) {
635 return SkIRect::MakeEmpty();
636 }
Michael Ludwig915b7792019-10-22 17:40:41 +0000637 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
senorblancoafc7cce2016-02-02 18:44:15 -0800638}
639
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000640SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000642 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400644 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645}
646
Florin Malita0ed3b642017-01-13 16:56:38 +0000647SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400648 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000649}
650
Mike Reed353196f2017-07-21 11:01:18 -0400651bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000652 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400653 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000654}
655
Mike Reed353196f2017-07-21 11:01:18 -0400656bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
657 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400658}
659
660bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
661 SkPixmap pm;
662 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
663}
664
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000665bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400666 SkPixmap pm;
667 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700668 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000669 }
670 return false;
671}
672
Matt Sarett03dd6d52017-01-23 12:15:09 -0500673bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000675 SkBaseDevice* device = this->getDevice();
676 if (!device) {
677 return false;
678 }
679
Matt Sarett03dd6d52017-01-23 12:15:09 -0500680 // This check gives us an early out and prevents generation ID churn on the surface.
681 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
682 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400683 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500684 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000686
Matt Sarett03dd6d52017-01-23 12:15:09 -0500687 // Tell our owning surface to bump its generation ID.
688 const bool completeOverwrite =
689 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700690 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700691
Matt Sarett03dd6d52017-01-23 12:15:09 -0500692 // This can still fail, most notably in the case of a invalid color type or alpha type
693 // conversion. We could pull those checks into this function and avoid the unnecessary
694 // generation ID bump. But then we would be performing those checks twice, since they
695 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400696 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000697}
reed@google.com51df9e32010-12-23 19:29:18 +0000698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699//////////////////////////////////////////////////////////////////////////////
700
reed2ff1fce2014-12-11 07:07:37 -0800701void SkCanvas::checkForDeferredSave() {
702 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800703 this->doSave();
704 }
705}
706
reedf0090cb2014-11-26 08:55:51 -0800707int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800708#ifdef SK_DEBUG
709 int count = 0;
710 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
711 for (;;) {
712 const MCRec* rec = (const MCRec*)iter.next();
713 if (!rec) {
714 break;
715 }
716 count += 1 + rec->fDeferredSaveCount;
717 }
718 SkASSERT(count == fSaveCount);
719#endif
720 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800721}
722
723int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800724 fSaveCount += 1;
725 fMCRec->fDeferredSaveCount += 1;
726 return this->getSaveCount() - 1; // return our prev value
727}
728
729void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800730 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700731
732 SkASSERT(fMCRec->fDeferredSaveCount > 0);
733 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800734 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800735}
736
Mike Reed00a97642020-01-25 18:42:23 -0500737int SkCanvas::experimental_saveCamera(const SkM44& projection, const SkM44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500738 // TODO: add a virtual for this, and update clients (e.g. chrome)
739 int n = this->save();
Mike Reed46f5c5f2020-02-20 15:42:29 -0500740 this->concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500741 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500742 return n;
743}
744
Mike Reed00a97642020-01-25 18:42:23 -0500745int SkCanvas::experimental_saveCamera(const SkScalar projection[16],
746 const SkScalar camera[16]) {
747 SkM44 proj, cam;
748 proj.setColMajor(projection);
749 cam.setColMajor(camera);
750 return this->experimental_saveCamera(proj, cam);
751}
752
reedf0090cb2014-11-26 08:55:51 -0800753void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800754 if (fMCRec->fDeferredSaveCount > 0) {
755 SkASSERT(fSaveCount > 1);
756 fSaveCount -= 1;
757 fMCRec->fDeferredSaveCount -= 1;
758 } else {
759 // check for underflow
760 if (fMCStack.count() > 1) {
761 this->willRestore();
762 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700763 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800764 this->internalRestore();
765 this->didRestore();
766 }
reedf0090cb2014-11-26 08:55:51 -0800767 }
768}
769
770void SkCanvas::restoreToCount(int count) {
771 // sanity check
772 if (count < 1) {
773 count = 1;
774 }
mtkleinf0f14112014-12-12 08:46:25 -0800775
reedf0090cb2014-11-26 08:55:51 -0800776 int n = this->getSaveCount() - count;
777 for (int i = 0; i < n; ++i) {
778 this->restore();
779 }
780}
781
reed2ff1fce2014-12-11 07:07:37 -0800782void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700784 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000786
Mike Reedc42a1cd2017-02-14 14:25:14 -0500787 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788}
789
reed4960eee2015-12-18 07:09:18 -0800790bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400791 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792}
793
reed4960eee2015-12-18 07:09:18 -0800794bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700795 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400796 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
797 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
798 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
799 // filter does not require a decomposed CTM matrix, the filter space and device space are the
800 // same. When it has been decomposed, we want the original image filter node to process the
801 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
802 // longer the original filter, but has the remainder matrix baked into it, and passing in the
803 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
804 // to the original filter node (barring inflation from consecutive calls to mapRect). While
805 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
806 // passing getDeviceClipBounds() to 'imageFilter' is correct.
807 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
808 // be important to more accurately calculate the clip bounds in the layer space for the original
809 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500810 SkIRect clipBounds = this->getDeviceClipBounds();
811 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000812 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000813 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000814
reed96e657d2015-03-10 17:30:07 -0700815 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
816
Robert Phillips12078432018-05-17 11:17:39 -0400817 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
818 // If the image filter DAG affects transparent black then we will need to render
819 // out to the clip bounds
820 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000821 }
Robert Phillips12078432018-05-17 11:17:39 -0400822
823 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700824 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700826 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400827 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400829 inputSaveLayerBounds = clipBounds;
830 }
831
832 if (imageFilter) {
833 // expand the clip bounds by the image filter DAG to include extra content that might
834 // be required by the image filters.
835 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
836 SkImageFilter::kReverse_MapDirection,
837 &inputSaveLayerBounds);
838 }
839
840 SkIRect clippedSaveLayerBounds;
841 if (bounds) {
842 // For better or for worse, user bounds currently act as a hard clip on the layer's
843 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
844 clippedSaveLayerBounds = inputSaveLayerBounds;
845 } else {
846 // If there are no user bounds, we don't want to artificially restrict the resulting
847 // layer bounds, so allow the expanded clip bounds free reign.
848 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800850
851 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400852 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800853 if (BoundsAffectsClip(saveLayerFlags)) {
854 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
855 fMCRec->fRasterClip.setEmpty();
856 fDeviceClipBounds.setEmpty();
857 }
858 return false;
859 }
Robert Phillips12078432018-05-17 11:17:39 -0400860 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861
reed4960eee2015-12-18 07:09:18 -0800862 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700863 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400864 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
865 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000866 }
867
868 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400869 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870 }
Robert Phillips12078432018-05-17 11:17:39 -0400871
junov@chromium.orga907ac32012-02-24 21:54:07 +0000872 return true;
873}
874
reed4960eee2015-12-18 07:09:18 -0800875int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
876 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000877}
878
Cary Clarke041e312018-03-06 13:00:52 -0500879int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700880 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400881 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
882 // no need for the layer (or any of the draws until the matching restore()
883 this->save();
884 this->clipRect({0,0,0,0});
885 } else {
886 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
887 fSaveCount += 1;
888 this->internalSaveLayer(rec, strategy);
889 }
reed4960eee2015-12-18 07:09:18 -0800890 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800891}
892
Mike Reed148b7fd2018-12-18 17:38:18 -0500893int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
894 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
895 // Assuming clips never expand, if the request bounds is outside of the current clip
896 // there is no need to copy/restore the area, so just devolve back to a regular save.
897 this->save();
898 } else {
899 bool doTheWork = this->onDoSaveBehind(bounds);
900 fSaveCount += 1;
901 this->internalSave();
902 if (doTheWork) {
903 this->internalSaveBehind(bounds);
904 }
905 }
906 return this->getSaveCount() - 1;
907}
908
reeda2217ef2016-07-20 06:04:34 -0700909void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500910 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500911 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400912 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
913 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400914 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig915b7792019-10-22 17:40:41 +0000915
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400916 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400917 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
918 // This means that we only have to copy a dst-sized block of pixels out of src and translate
919 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400920 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
921 dstOrigin.y() - src->getOrigin().y(),
922 dst->width(), dst->height());
923 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400924 return;
925 }
926
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400927 auto special = src->snapSpecial(snapBounds);
928 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400929 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
930 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400931 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
932 }
933 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400934 }
935
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400936 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
937 // by the backdrop filter.
938 SkMatrix toRoot, layerMatrix;
939 SkSize scale;
940 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
941 toRoot = SkMatrix::I();
942 layerMatrix = ctm;
943 } else if (ctm.decomposeScale(&scale, &toRoot)) {
944 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
945 } else {
946 // Perspective, for now, do no scaling of the layer itself.
947 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
948 // the matrix, e.g. based on the midpoint of the near/far planes?
949 toRoot = ctm;
950 layerMatrix = SkMatrix::I();
951 }
952
953 // We have to map the dst bounds from the root space into the layer space where filtering will
954 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
955 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
956 // is a safe, conservative estimate.
957 SkMatrix fromRoot;
958 if (!toRoot.invert(&fromRoot)) {
959 return;
960 }
961
962 // This represents what the backdrop filter needs to produce in the layer space, and is sized
963 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
964 SkIRect layerTargetBounds = fromRoot.mapRect(
965 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
966 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
967 // require some extra input pixels.
968 SkIRect layerInputBounds = filter->filterBounds(
969 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
970 &layerTargetBounds);
971
972 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400973 // be the conservative contents required to fill a layerInputBounds-sized surface with the
974 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400975 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
976 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
977 if (!backdropBounds.intersect(srcDevRect)) {
978 return;
979 }
980
981 auto special = src->snapSpecial(backdropBounds);
982 if (!special) {
983 return;
984 }
985
986 SkColorType colorType = src->imageInfo().colorType();
987 if (colorType == kUnknown_SkColorType) {
988 colorType = kRGBA_8888_SkColorType;
989 }
990 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400991
992 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400993 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400994 // Drawing the temporary and final filtered image requires a higher filter quality if the
995 // 'toRoot' transformation is not identity, in order to minimize the impact on already
996 // rendered edges/content.
997 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
998 p.setFilterQuality(kHigh_SkFilterQuality);
999
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001000 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
1001 // and stored in a temporary surface, which is then used as the input to the actual filter.
1002 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
1003 if (!tmpSurface) {
1004 return;
1005 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001006
1007 auto tmpCanvas = tmpSurface->getCanvas();
1008 tmpCanvas->clear(SK_ColorTRANSPARENT);
1009 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1010 // space, then maps from root space into the layer space, then maps it so the input layer's
1011 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1012 // performed on backdropBounds.
1013 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1014 tmpCanvas->concat(fromRoot);
1015 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001016
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001017 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1018 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1019 special = tmpSurface->makeImageSnapshot();
1020 } else {
1021 // Since there is no extra transform that was done, update the input bounds to reflect
1022 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1023 // was equal to backdropBounds before it was made relative to the src device and cropped.
1024 // When we use the original snapped image directly, just map the update backdrop bounds
1025 // back into the shared layer space
1026 layerInputBounds = backdropBounds;
1027 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001028
1029 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1030 // draw will be 1-1 so there is no need to increase filter quality.
1031 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001032 }
1033
1034 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1035 // layer space. This has to further offset everything so that filter evaluation thinks the
1036 // source image's top left corner is (0, 0).
1037 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1038 // this can be simplified.
1039 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1040 SkMatrix filterCTM = layerMatrix;
1041 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1042 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1043
1044 SkIPoint offset;
1045 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001046 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001047 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1048 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1049 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1050 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001051 offset += layerInputBounds.topLeft();
1052
1053 // Manually setting the device's CTM requires accounting for the device's origin.
1054 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001055 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001056 // a global CTM instead of a device CTM.
1057 SkMatrix dstCTM = toRoot;
1058 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001059 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001060
1061 // And because devices don't have a special-image draw function that supports arbitrary
1062 // matrices, we are abusing the asImage() functionality here...
1063 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001064 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001065 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001066 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001067 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1068 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001069 }
robertphillips7354a4b2015-12-16 05:08:27 -08001070}
reed70ee31b2015-12-10 13:44:45 -08001071
Mike Kleine083f7c2018-02-07 12:54:27 -05001072static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001073 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001074 if (prev.bytesPerPixel() <= 4 &&
1075 prev.colorType() != kRGBA_8888_SkColorType &&
1076 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001077 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1078 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1079 ct = kN32_SkColorType;
1080 }
1081 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001082}
1083
reed4960eee2015-12-18 07:09:18 -08001084void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001085 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001086 const SkRect* bounds = rec.fBounds;
1087 const SkPaint* paint = rec.fPaint;
1088 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1089
Mike Reed5532c2a2019-02-23 12:00:32 -05001090 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1091 // regardless of any hint-rect from the caller. skbug.com/8783
1092 if (rec.fBackdrop) {
1093 bounds = nullptr;
1094 }
1095
reed8c30a812016-04-20 16:36:51 -07001096 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001097 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001098 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001099 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001100
reed8c30a812016-04-20 16:36:51 -07001101 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001102 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1103 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1104 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001105 *
1106 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001107 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1108 * if necessary.
1109 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1110 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001111 * 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 -04001112 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001113 * of the original imagefilter, and draw that (via drawSprite)
1114 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1115 *
1116 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1117 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1118 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001119 if (imageFilter) {
1120 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001121 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1122 &modifiedCTM);
1123 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001124 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001125 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001126 modifiedRec = fMCRec;
1127 this->internalSetMatrix(modifiedCTM);
1128 SkPaint* p = lazyP.set(*paint);
1129 p->setImageFilter(std::move(modifiedFilter));
1130 imageFilter = p->getImageFilter();
1131 paint = p;
1132 }
1133 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1134 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001135 }
reed8c30a812016-04-20 16:36:51 -07001136
junov@chromium.orga907ac32012-02-24 21:54:07 +00001137 // do this before we create the layer. We don't call the public save() since
1138 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001139 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001140
junov@chromium.orga907ac32012-02-24 21:54:07 +00001141 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001142 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001143 if (modifiedRec) {
1144 // In this case there will be no layer in which to stash the matrix so we need to
1145 // revert the prior MCRec to its earlier state.
1146 modifiedRec->fMatrix = stashedMatrix;
1147 }
reed2ff1fce2014-12-11 07:07:37 -08001148 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 }
1150
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001151 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1152 // the clipRectBounds() call above?
1153 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001154 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001155 }
1156
reed8dc0ccb2015-03-20 06:32:52 -07001157 SkPixelGeometry geo = fProps.pixelGeometry();
1158 if (paint) {
reed76033be2015-03-14 10:54:31 -07001159 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001160 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001161 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001162 }
1163 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164
robertphillips5139e502016-07-19 05:10:40 -07001165 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001166 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001167 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001168 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001169 }
reedb2db8982014-11-13 12:41:02 -08001170
Mike Kleine083f7c2018-02-07 12:54:27 -05001171 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001172 if (rec.fSaveLayerFlags & kF16ColorType) {
1173 info = info.makeColorType(kRGBA_F16_SkColorType);
1174 }
reed129ed1c2016-02-22 06:42:31 -08001175
Hal Canary704cd322016-11-07 14:13:52 -05001176 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001177 {
Florin Malita4571e492019-07-16 10:25:58 -04001178 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001179 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001180 const bool trackCoverage =
1181 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001182 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001183 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001184 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001185 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1186 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001187 return;
reed61f501f2015-04-29 08:34:00 -07001188 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001189 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001190 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191
Mike Reedb43a3e02017-02-11 10:18:58 -05001192 // only have a "next" if this new layer doesn't affect the clip (rare)
1193 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 fMCRec->fLayer = layer;
1195 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001196
Mike Reedc61abee2017-02-28 17:45:27 -05001197 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001198 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001199 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001200 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001201
Mike Reedc42a1cd2017-02-14 14:25:14 -05001202 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1203
1204 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1205 if (layer->fNext) {
1206 // need to punch a hole in the previous device, so we don't draw there, given that
1207 // the new top-layer will allow drawing to happen "below" it.
1208 SkRegion hole(ir);
1209 do {
1210 layer = layer->fNext;
1211 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1212 } while (layer->fNext);
1213 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214}
1215
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001216int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001217 if (0xFF == alpha) {
1218 return this->saveLayer(bounds, nullptr);
1219 } else {
1220 SkPaint tmpPaint;
1221 tmpPaint.setAlpha(alpha);
1222 return this->saveLayer(bounds, &tmpPaint);
1223 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001224}
1225
Mike Reed148b7fd2018-12-18 17:38:18 -05001226void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001227 SkIRect devBounds;
1228 if (localBounds) {
1229 SkRect tmp;
1230 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1231 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1232 devBounds.setEmpty();
1233 }
1234 } else {
1235 devBounds = this->getDeviceClipBounds();
1236 }
1237 if (devBounds.isEmpty()) {
1238 return;
1239 }
1240
Mike Reed148b7fd2018-12-18 17:38:18 -05001241 SkBaseDevice* device = this->getTopDevice();
1242 if (nullptr == device) { // Do we still need this check???
1243 return;
1244 }
1245
Michael Ludwig915b7792019-10-22 17:40:41 +00001246 // need the bounds relative to the device itself
1247 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
Mike Reed148b7fd2018-12-18 17:38:18 -05001248
Michael Ludwigac352122019-08-28 21:03:05 +00001249 // This is getting the special image from the current device, which is then drawn into (both by
1250 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1251 // own device, we need to explicitly copy the back image contents so that its original content
1252 // is available when we splat it back later during restore.
1253 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001254 if (!backImage) {
1255 return;
1256 }
1257
1258 // we really need the save, so we can wack the fMCRec
1259 this->checkForDeferredSave();
1260
1261 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1262
1263 SkPaint paint;
1264 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001265 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001266}
1267
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268void SkCanvas::internalRestore() {
1269 SkASSERT(fMCStack.count() != 0);
1270
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001271 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 DeviceCM* layer = fMCRec->fLayer; // may be null
1273 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001274 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275
Mike Reed148b7fd2018-12-18 17:38:18 -05001276 // move this out before we do the actual restore
1277 auto backImage = std::move(fMCRec->fBackImage);
1278
Mike Reedb18e74d2020-01-16 13:58:22 -05001279 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1280 fCameraStack.pop_back();
1281 }
1282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 // now do the normal restore()
1284 fMCRec->~MCRec(); // balanced in save()
1285 fMCStack.pop_back();
1286 fMCRec = (MCRec*)fMCStack.back();
1287
Mike Reedc42a1cd2017-02-14 14:25:14 -05001288 if (fMCRec) {
1289 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1290 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001291
Mike Reed148b7fd2018-12-18 17:38:18 -05001292 if (backImage) {
1293 SkPaint paint;
1294 paint.setBlendMode(SkBlendMode::kDstOver);
1295 const int x = backImage->fLoc.x();
1296 const int y = backImage->fLoc.y();
1297 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1298 nullptr, SkMatrix::I());
1299 }
1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1302 since if we're being recorded, we don't want to record this (the
1303 recorder will have already recorded the restore).
1304 */
bsalomon49f085d2014-09-05 13:34:00 -07001305 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001306 if (fMCRec) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001307 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001308 layer->fDevice->setImmutable();
Michael Ludwig915b7792019-10-22 17:40:41 +00001309 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
1310 layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001311 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001312 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001313 this->internalSetMatrix(layer->fStashedMatrix);
Michael Ludwig915b7792019-10-22 17:40:41 +00001314 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001315 delete layer;
reedb679ca82015-04-07 04:40:48 -07001316 } else {
1317 // we're at the root
reeda499f902015-05-01 09:34:31 -07001318 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001319 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001320 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001322 }
msarettfbfa2582016-08-12 08:29:08 -07001323
1324 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001325 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001326 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1327 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328}
1329
reede8f30622016-03-23 18:59:25 -07001330sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001331 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001332 props = &fProps;
1333 }
1334 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001335}
1336
reede8f30622016-03-23 18:59:25 -07001337sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001338 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001339 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001340}
1341
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001342SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001343 return this->onImageInfo();
1344}
1345
1346SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001347 SkBaseDevice* dev = this->getDevice();
1348 if (dev) {
1349 return dev->imageInfo();
1350 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001351 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001352 }
1353}
1354
brianosman898235c2016-04-06 07:38:23 -07001355bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001356 return this->onGetProps(props);
1357}
1358
1359bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001360 SkBaseDevice* dev = this->getDevice();
1361 if (dev) {
1362 if (props) {
1363 *props = fProps;
1364 }
1365 return true;
1366 } else {
1367 return false;
1368 }
1369}
1370
reed6ceeebd2016-03-09 14:26:26 -08001371bool SkCanvas::peekPixels(SkPixmap* pmap) {
1372 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373}
1374
reed884e97c2015-05-26 11:31:54 -07001375bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001376 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001377 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001378}
1379
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001380void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001381 SkPixmap pmap;
1382 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001383 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001384 }
1385 if (info) {
1386 *info = pmap.info();
1387 }
1388 if (rowBytes) {
1389 *rowBytes = pmap.rowBytes();
1390 }
1391 if (origin) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001392 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001393 }
reed884e97c2015-05-26 11:31:54 -07001394 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001395}
1396
reed884e97c2015-05-26 11:31:54 -07001397bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001398 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001399 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001400}
1401
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403
Mike Reed8bcd1282019-03-13 16:51:54 -04001404// In our current design/features, we should never have a layer (src) in a different colorspace
1405// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1406// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1407// colorspace.
1408static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1409 SkASSERT(src == dst);
1410}
1411
Michael Ludwig915b7792019-10-22 17:40:41 +00001412void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001413 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001415 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 paint = &tmp;
1417 }
reed@google.com4b226022011-01-11 18:32:13 +00001418
Mike Reed38992392019-07-30 10:48:15 -04001419 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001420
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001422 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001423 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1424 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001425 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001426 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwig915b7792019-10-22 17:40:41 +00001427 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001428 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001429 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1430 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001431 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1432 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001433 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1434 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001435 }
reed@google.com76dd2772012-01-05 21:15:07 +00001436 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001437 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001438 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 }
reeda2217ef2016-07-20 06:04:34 -07001440
Mike Reed38992392019-07-30 10:48:15 -04001441 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442}
1443
reed32704672015-12-16 08:27:10 -08001444/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001445
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001446void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001447 if (dx || dy) {
1448 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001449 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001450
reedfe69b502016-09-12 06:31:48 -07001451 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001452 // However, if either is non-finite, we might still complicate the matrix type,
1453 // so we still have to compute this.
1454 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001455
Mike Reedc42a1cd2017-02-14 14:25:14 -05001456 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001457
reedfe69b502016-09-12 06:31:48 -07001458 this->didTranslate(dx,dy);
1459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460}
1461
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001462void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001463 if (sx != 1 || sy != 1) {
1464 this->checkForDeferredSave();
1465 fMCRec->fMatrix.preScale(sx, sy);
1466
1467 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1468 // but pre-scaling by a non-finite does change it, so we have to recompute.
1469 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1470
1471 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1472
1473 this->didScale(sx, sy);
1474 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475}
1476
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001477void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001478 SkMatrix m;
1479 m.setRotate(degrees);
1480 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481}
1482
bungeman7438bfc2016-07-12 15:01:19 -07001483void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1484 SkMatrix m;
1485 m.setRotate(degrees, px, py);
1486 this->concat(m);
1487}
1488
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001489void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001490 SkMatrix m;
1491 m.setSkew(sx, sy);
1492 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001493}
1494
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001495void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001496 if (matrix.isIdentity()) {
1497 return;
1498 }
1499
reed2ff1fce2014-12-11 07:07:37 -08001500 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001501 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001502
msarett9637ea92016-08-18 14:03:30 -07001503 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001504
Mike Reed7627fa52017-02-08 10:07:53 -05001505 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001506
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001507 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001508}
1509
Mike Reed46f5c5f2020-02-20 15:42:29 -05001510void SkCanvas::concat44(const SkScalar colMajor[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001511 this->checkForDeferredSave();
1512
Mike Reed46f5c5f2020-02-20 15:42:29 -05001513 fMCRec->fMatrix.preConcat16(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001514
1515 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1516
1517 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1518
Mike Reed46f5c5f2020-02-20 15:42:29 -05001519 this->didConcat44(colMajor);
Mike Reed403c8072020-01-08 10:40:39 -05001520}
Mike Reed064c7f92020-01-08 17:33:04 -05001521
Mike Reed46f5c5f2020-02-20 15:42:29 -05001522void SkCanvas::concat44(const SkM44& m) {
1523 this->concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001524}
1525
reed8c30a812016-04-20 16:36:51 -07001526void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001527 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001528 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001529
Mike Reedc42a1cd2017-02-14 14:25:14 -05001530 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001531}
1532
1533void SkCanvas::setMatrix(const SkMatrix& matrix) {
1534 this->checkForDeferredSave();
1535 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001536 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537}
1538
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001540 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541}
1542
1543//////////////////////////////////////////////////////////////////////////////
1544
Mike Reedc1f77742016-12-09 09:00:50 -05001545void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001546 if (!rect.isFinite()) {
1547 return;
1548 }
reed2ff1fce2014-12-11 07:07:37 -08001549 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001550 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1551 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001552}
1553
Mike Reedc1f77742016-12-09 09:00:50 -05001554void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001555 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001556
Mike Reed7627fa52017-02-08 10:07:53 -05001557 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001558
reedc64eff52015-11-21 12:39:45 -08001559 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001560 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1561 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001562 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563}
1564
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001565void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1566 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001567 if (fClipRestrictionRect.isEmpty()) {
1568 // we notify the device, but we *dont* resolve deferred saves (since we're just
1569 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001570 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001571 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001572 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001573 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001574 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001575 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001576 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1577 }
1578}
1579
Mike Reedc1f77742016-12-09 09:00:50 -05001580void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001581 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001582 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001583 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001584 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1585 } else {
1586 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001587 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001588}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001589
Mike Reedc1f77742016-12-09 09:00:50 -05001590void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001591 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001592
Brian Salomona3b45d42016-10-03 11:36:16 -04001593 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001594
Mike Reed7627fa52017-02-08 10:07:53 -05001595 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001596
Mike Reed20800c82017-11-15 16:09:04 -05001597 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1598 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001599 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001600}
1601
Mike Reedc1f77742016-12-09 09:00:50 -05001602void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001603 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001604 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001605
1606 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1607 SkRect r;
1608 if (path.isRect(&r)) {
1609 this->onClipRect(r, op, edgeStyle);
1610 return;
1611 }
1612 SkRRect rrect;
1613 if (path.isOval(&r)) {
1614 rrect.setOval(r);
1615 this->onClipRRect(rrect, op, edgeStyle);
1616 return;
1617 }
1618 if (path.isRRect(&rrect)) {
1619 this->onClipRRect(rrect, op, edgeStyle);
1620 return;
1621 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622 }
robertphillips39f05382015-11-24 09:30:12 -08001623
1624 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001625}
1626
Mike Reedc1f77742016-12-09 09:00:50 -05001627void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001628 AutoValidateClip avc(this);
1629
Brian Salomona3b45d42016-10-03 11:36:16 -04001630 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001631
Mike Reed7627fa52017-02-08 10:07:53 -05001632 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633
Brian Salomona3b45d42016-10-03 11:36:16 -04001634 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001635 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001636 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001637 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638}
1639
Mike Reedc1f77742016-12-09 09:00:50 -05001640void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001641 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001642 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001643}
1644
Mike Reedc1f77742016-12-09 09:00:50 -05001645void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001646 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001647
reed@google.com5c3d1472011-02-22 19:12:23 +00001648 AutoValidateClip avc(this);
1649
Mike Reed20800c82017-11-15 16:09:04 -05001650 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001651 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652}
1653
reed@google.com819c9212011-02-23 18:56:55 +00001654#ifdef SK_DEBUG
1655void SkCanvas::validateClip() const {
1656 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001657 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001658 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001659 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001660 return;
1661 }
reed@google.com819c9212011-02-23 18:56:55 +00001662}
1663#endif
1664
Mike Reeda1361362017-03-07 09:37:29 -05001665bool SkCanvas::androidFramework_isClipAA() const {
1666 bool containsAA = false;
1667
1668 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1669
1670 return containsAA;
1671}
1672
1673class RgnAccumulator {
1674 SkRegion* fRgn;
1675public:
1676 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1677 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1678 SkIPoint origin = device->getOrigin();
1679 if (origin.x() | origin.y()) {
1680 rgn->translate(origin.x(), origin.y());
1681 }
1682 fRgn->op(*rgn, SkRegion::kUnion_Op);
1683 }
1684};
1685
1686void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1687 RgnAccumulator accum(rgn);
1688 SkRegion tmp;
1689
1690 rgn->setEmpty();
1691 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001692}
1693
reed@google.com5c3d1472011-02-22 19:12:23 +00001694///////////////////////////////////////////////////////////////////////////////
1695
reed@google.com754de5f2014-02-24 19:38:20 +00001696bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001697 return fMCRec->fRasterClip.isEmpty();
1698
1699 // TODO: should we only use the conservative answer in a recording canvas?
1700#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001701 SkBaseDevice* dev = this->getTopDevice();
1702 // if no device we return true
1703 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001704#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001705}
1706
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001707bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001708 SkBaseDevice* dev = this->getTopDevice();
1709 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001710 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001711}
1712
msarettfbfa2582016-08-12 08:29:08 -07001713static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1714#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1715 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1716 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1717 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1718 return 0xF != _mm_movemask_ps(mask);
1719#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1720 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1721 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1722 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1723 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1724#else
1725 SkRect devRectAsRect;
1726 SkRect devClipAsRect;
1727 devRect.store(&devRectAsRect.fLeft);
1728 devClip.store(&devClipAsRect.fLeft);
1729 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1730#endif
1731}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001732
msarettfbfa2582016-08-12 08:29:08 -07001733// It's important for this function to not be inlined. Otherwise the compiler will share code
1734// between the fast path and the slow path, resulting in two slow paths.
1735static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1736 const SkMatrix& matrix) {
1737 SkRect deviceRect;
1738 matrix.mapRect(&deviceRect, src);
1739 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1740}
1741
1742bool SkCanvas::quickReject(const SkRect& src) const {
1743#ifdef SK_DEBUG
1744 // Verify that fDeviceClipBounds are set properly.
1745 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001746 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001747 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001748 } else {
msarettfbfa2582016-08-12 08:29:08 -07001749 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750 }
msarettfbfa2582016-08-12 08:29:08 -07001751
msarett9637ea92016-08-18 14:03:30 -07001752 // Verify that fIsScaleTranslate is set properly.
1753 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001754#endif
1755
msarett9637ea92016-08-18 14:03:30 -07001756 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001757 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1758 }
1759
1760 // We inline the implementation of mapScaleTranslate() for the fast path.
1761 float sx = fMCRec->fMatrix.getScaleX();
1762 float sy = fMCRec->fMatrix.getScaleY();
1763 float tx = fMCRec->fMatrix.getTranslateX();
1764 float ty = fMCRec->fMatrix.getTranslateY();
1765 Sk4f scale(sx, sy, sx, sy);
1766 Sk4f trans(tx, ty, tx, ty);
1767
1768 // Apply matrix.
1769 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1770
1771 // Make sure left < right, top < bottom.
1772 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1773 Sk4f min = Sk4f::Min(ltrb, rblt);
1774 Sk4f max = Sk4f::Max(ltrb, rblt);
1775 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1776 // ARM this sequence generates the fastest (a single instruction).
1777 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1778
1779 // Check if the device rect is NaN or outside the clip.
1780 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781}
1782
reed@google.com3b3e8952012-08-16 20:53:31 +00001783bool SkCanvas::quickReject(const SkPath& path) const {
1784 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785}
1786
Mike Klein83c8dd92017-11-28 17:08:45 -05001787SkRect SkCanvas::getLocalClipBounds() const {
1788 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001789 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001790 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791 }
1792
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001793 SkMatrix inverse;
1794 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001795 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001796 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001797 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798
Mike Reed42e8c532017-01-23 14:09:13 -05001799 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001800 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001801 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001802
Mike Reedb57b9312018-04-23 12:12:54 -04001803 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001804 inverse.mapRect(&bounds, r);
1805 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806}
1807
Mike Klein83c8dd92017-11-28 17:08:45 -05001808SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001809 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001810}
1811
Mike Reedb18e74d2020-01-16 13:58:22 -05001812///////////////////////////////////////////////////////////////////////
1813
1814SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1815 : fMCRec(owner)
1816 , fCamera(camera)
1817{
1818 // assumes the mcrec has already been concatenated with the camera
1819 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1820 fInvPostCamera.setIdentity();
1821 }
1822}
1823
Mike Reed403c8072020-01-08 10:40:39 -05001824SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001825 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826}
1827
Mike Reed46f5c5f2020-02-20 15:42:29 -05001828SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001829 return fMCRec->fMatrix;
1830}
1831
Mike Reedc43f2a02020-01-16 14:54:34 -05001832SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001833 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001834 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001835 } else {
1836 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001837 return top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001838 }
1839}
1840
Mike Reedc43f2a02020-01-16 14:54:34 -05001841SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001842 if (fCameraStack.empty()) {
Mike Reed46f5c5f2020-02-20 15:42:29 -05001843 return this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001844 } else {
1845 const auto& top = fCameraStack.back();
Mike Reed46f5c5f2020-02-20 15:42:29 -05001846 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001847 }
1848}
1849
Mike Reed46f5c5f2020-02-20 15:42:29 -05001850void SkCanvas::getLocalToDevice(SkScalar colMajor[16]) const {
1851 this->getLocalToDevice().getColMajor(colMajor);
Mike Reedd3963a32020-01-26 13:08:45 -05001852}
1853
1854void SkCanvas::experimental_getLocalToWorld(SkScalar colMajor[16]) const {
1855 this->experimental_getLocalToWorld().getColMajor(colMajor);
1856}
1857
1858void SkCanvas::experimental_getLocalToCamera(SkScalar colMajor[16]) const {
1859 this->experimental_getLocalToCamera().getColMajor(colMajor);
1860}
1861
Brian Osman11052242016-10-27 14:47:55 -04001862GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001863 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001864 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001865}
1866
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001867GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001868 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001869 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001870}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001871
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001872void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1873 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001874 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001875 if (outer.isEmpty()) {
1876 return;
1877 }
1878 if (inner.isEmpty()) {
1879 this->drawRRect(outer, paint);
1880 return;
1881 }
1882
1883 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001884 // be able to return ...
1885 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001886 //
1887 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001888 if (!outer.getBounds().contains(inner.getBounds())) {
1889 return;
1890 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001891
1892 this->onDrawDRRect(outer, inner, paint);
1893}
1894
reed41af9662015-01-05 07:49:08 -08001895void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001896 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001897 this->onDrawPaint(paint);
1898}
1899
1900void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001901 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001902 // To avoid redundant logic in our culling code and various backends, we always sort rects
1903 // before passing them along.
1904 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001905}
1906
Mike Reedd5674082019-04-19 15:00:47 -04001907void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1908 TRACE_EVENT0("skia", TRACE_FUNC);
1909 this->onDrawBehind(paint);
1910}
1911
msarettdca352e2016-08-26 06:37:45 -07001912void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001913 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001914 if (region.isEmpty()) {
1915 return;
1916 }
1917
1918 if (region.isRect()) {
1919 return this->drawIRect(region.getBounds(), paint);
1920 }
1921
1922 this->onDrawRegion(region, paint);
1923}
1924
reed41af9662015-01-05 07:49:08 -08001925void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001926 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001927 // To avoid redundant logic in our culling code and various backends, we always sort rects
1928 // before passing them along.
1929 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001930}
1931
1932void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001933 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001934 this->onDrawRRect(rrect, paint);
1935}
1936
1937void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001938 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001939 this->onDrawPoints(mode, count, pts, paint);
1940}
1941
Mike Reede88a1cb2017-03-17 09:50:46 -04001942void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1943 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001944 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001945 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001946 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1947 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001948 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001949}
1950
1951void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001952 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001953 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001954 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1955}
1956
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001957void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1958 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001959 TRACE_EVENT0("skia", TRACE_FUNC);
1960 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001961 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001962 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1963}
1964
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001965void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1966 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001967 TRACE_EVENT0("skia", TRACE_FUNC);
1968 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001969 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001970 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001971}
1972
1973void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001974 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001975 this->onDrawPath(path, paint);
1976}
1977
reeda85d4d02015-05-06 12:56:48 -07001978void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001979 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001980 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001981 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001982}
1983
Mike Reedc4e31092018-01-30 11:15:27 -05001984// Returns true if the rect can be "filled" : non-empty and finite
1985static bool fillable(const SkRect& r) {
1986 SkScalar w = r.width();
1987 SkScalar h = r.height();
1988 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1989}
1990
reede47829b2015-08-06 10:02:53 -07001991void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1992 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001993 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001994 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001995 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001996 return;
1997 }
1998 this->onDrawImageRect(image, &src, dst, paint, constraint);
1999}
reed41af9662015-01-05 07:49:08 -08002000
reed84984ef2015-07-17 07:09:43 -07002001void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2002 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002003 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002004 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002005}
2006
Brian Salomonf08002c2018-10-26 16:15:46 -04002007void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002008 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002009 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04002010 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002011}
reede47829b2015-08-06 10:02:53 -07002012
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002013namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002014class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002015public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002016 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2017 if (!origPaint) {
2018 return;
2019 }
2020 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2021 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2022 }
2023 if (origPaint->getMaskFilter()) {
2024 fPaint.writable()->setMaskFilter(nullptr);
2025 }
2026 if (origPaint->isAntiAlias()) {
2027 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002028 }
2029 }
2030
2031 const SkPaint* get() const {
2032 return fPaint;
2033 }
2034
2035private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002036 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002037};
2038} // namespace
2039
reed4c21dc52015-06-25 12:32:03 -07002040void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2041 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002042 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002043 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002044 if (dst.isEmpty()) {
2045 return;
2046 }
msarett552bca92016-08-03 06:53:26 -07002047 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002048 LatticePaint latticePaint(paint);
2049 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002050 } else {
reede47829b2015-08-06 10:02:53 -07002051 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002052 }
reed4c21dc52015-06-25 12:32:03 -07002053}
2054
msarett16882062016-08-16 09:31:08 -07002055void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2056 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002057 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002058 RETURN_ON_NULL(image);
2059 if (dst.isEmpty()) {
2060 return;
2061 }
msarett71df2d72016-09-30 12:41:42 -07002062
2063 SkIRect bounds;
2064 Lattice latticePlusBounds = lattice;
2065 if (!latticePlusBounds.fBounds) {
2066 bounds = SkIRect::MakeWH(image->width(), image->height());
2067 latticePlusBounds.fBounds = &bounds;
2068 }
2069
2070 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002071 LatticePaint latticePaint(paint);
2072 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002073 } else {
2074 this->drawImageRect(image, dst, paint);
2075 }
2076}
2077
reed41af9662015-01-05 07:49:08 -08002078void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002079 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002080 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002081 return;
2082 }
reed41af9662015-01-05 07:49:08 -08002083 this->onDrawBitmap(bitmap, dx, dy, paint);
2084}
2085
reede47829b2015-08-06 10:02:53 -07002086void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002087 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002088 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002089 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002090 return;
2091 }
reede47829b2015-08-06 10:02:53 -07002092 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002093}
2094
reed84984ef2015-07-17 07:09:43 -07002095void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2096 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002097 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002098}
2099
reede47829b2015-08-06 10:02:53 -07002100void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2101 SrcRectConstraint constraint) {
2102 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2103 constraint);
2104}
reede47829b2015-08-06 10:02:53 -07002105
reed41af9662015-01-05 07:49:08 -08002106void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2107 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002108 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002109 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002110 return;
2111 }
msarett552bca92016-08-03 06:53:26 -07002112 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002113 LatticePaint latticePaint(paint);
2114 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002115 } else {
reeda5517e22015-07-14 10:54:12 -07002116 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002117 }
reed41af9662015-01-05 07:49:08 -08002118}
2119
msarettc573a402016-08-02 08:05:56 -07002120void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2121 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002122 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002123 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002124 return;
2125 }
msarett71df2d72016-09-30 12:41:42 -07002126
2127 SkIRect bounds;
2128 Lattice latticePlusBounds = lattice;
2129 if (!latticePlusBounds.fBounds) {
2130 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2131 latticePlusBounds.fBounds = &bounds;
2132 }
2133
2134 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002135 LatticePaint latticePaint(paint);
2136 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002137 } else {
msarett16882062016-08-16 09:31:08 -07002138 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002139 }
msarettc573a402016-08-02 08:05:56 -07002140}
2141
reed71c3c762015-06-24 10:29:17 -07002142void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002143 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002144 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002145 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002146 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002147 if (count <= 0) {
2148 return;
2149 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002150 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002151 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002152 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002153}
2154
reedf70b5312016-03-04 16:36:20 -08002155void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002156 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002157 if (key) {
2158 this->onDrawAnnotation(rect, key, value);
2159 }
2160}
2161
reede47829b2015-08-06 10:02:53 -07002162void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2163 const SkPaint* paint, SrcRectConstraint constraint) {
2164 if (src) {
2165 this->drawImageRect(image, *src, dst, paint, constraint);
2166 } else {
2167 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2168 dst, paint, constraint);
2169 }
2170}
2171void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2172 const SkPaint* paint, SrcRectConstraint constraint) {
2173 if (src) {
2174 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2175 } else {
2176 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2177 dst, paint, constraint);
2178 }
2179}
2180
Mike Reed4204da22017-05-17 08:53:36 -04002181void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002182 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002183 this->onDrawShadowRec(path, rec);
2184}
2185
2186void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2187 SkPaint paint;
2188 const SkRect& pathBounds = path.getBounds();
2189
Mike Reed38992392019-07-30 10:48:15 -04002190 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002191 while (iter.next()) {
2192 iter.fDevice->drawShadow(path, rec);
2193 }
Mike Reed38992392019-07-30 10:48:15 -04002194 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002195}
2196
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002197void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002198 QuadAAFlags aaFlags, const SkColor4f& color,
2199 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002200 TRACE_EVENT0("skia", TRACE_FUNC);
2201 // Make sure the rect is sorted before passing it along
2202 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2203}
2204
2205void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2206 const SkPoint dstClips[],
2207 const SkMatrix preViewMatrices[],
2208 const SkPaint* paint,
2209 SrcRectConstraint constraint) {
2210 TRACE_EVENT0("skia", TRACE_FUNC);
2211 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2212}
2213
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214//////////////////////////////////////////////////////////////////////////////
2215// These are the virtual drawing methods
2216//////////////////////////////////////////////////////////////////////////////
2217
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002218void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002219 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002220 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2221 }
2222}
2223
reed41af9662015-01-05 07:49:08 -08002224void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002225 this->internalDrawPaint(paint);
2226}
2227
2228void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002229 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230
2231 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002232 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233 }
2234
Mike Reed38992392019-07-30 10:48:15 -04002235 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002236}
2237
reed41af9662015-01-05 07:49:08 -08002238void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2239 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240 if ((long)count <= 0) {
2241 return;
2242 }
2243
Mike Reed822128b2017-02-28 16:41:03 -05002244 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002245 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002246 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002247 // special-case 2 points (common for drawing a single line)
2248 if (2 == count) {
2249 r.set(pts[0], pts[1]);
2250 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002251 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002252 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002253 if (!r.isFinite()) {
2254 return;
2255 }
Mike Reed822128b2017-02-28 16:41:03 -05002256 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002257 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2258 return;
2259 }
2260 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002261 }
reed@google.coma584aed2012-05-16 14:06:02 +00002262
halcanary96fcdcc2015-08-27 07:41:13 -07002263 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264
Mike Reed38992392019-07-30 10:48:15 -04002265 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002266
reed@android.com8a1c16f2008-12-17 15:59:43 +00002267 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002268 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 }
reed@google.com4b226022011-01-11 18:32:13 +00002270
Mike Reed38992392019-07-30 10:48:15 -04002271 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272}
2273
reed4a167172016-08-18 17:15:25 -07002274static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002275 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002276}
2277
reed41af9662015-01-05 07:49:08 -08002278void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002279 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002281 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002282 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002283 return;
2284 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002285 }
reed@google.com4b226022011-01-11 18:32:13 +00002286
reed4a167172016-08-18 17:15:25 -07002287 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002288 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002289
reed4a167172016-08-18 17:15:25 -07002290 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002291 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002292 }
2293
Mike Reed38992392019-07-30 10:48:15 -04002294 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002295 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002296 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002297 SkDrawIter iter(this);
2298 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002299 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002300 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002301 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302}
2303
msarett44df6512016-08-25 13:54:30 -07002304void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002305 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002306 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002307 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002308 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2309 return;
2310 }
msarett44df6512016-08-25 13:54:30 -07002311 }
2312
Mike Reed38992392019-07-30 10:48:15 -04002313 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002314
2315 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002316 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002317 }
2318
Mike Reed38992392019-07-30 10:48:15 -04002319 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002320}
2321
Mike Reedd5674082019-04-19 15:00:47 -04002322void SkCanvas::onDrawBehind(const SkPaint& paint) {
2323 SkIRect bounds;
2324 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2325 for (;;) {
2326 const MCRec* rec = (const MCRec*)iter.prev();
2327 if (!rec) {
2328 return; // no backimages, so nothing to draw
2329 }
2330 if (rec->fBackImage) {
2331 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2332 rec->fBackImage->fImage->width(),
2333 rec->fBackImage->fImage->height());
2334 break;
2335 }
2336 }
2337
Mike Reed38992392019-07-30 10:48:15 -04002338 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002339
2340 while (iter.next()) {
2341 SkBaseDevice* dev = iter.fDevice;
2342
Mike Reedd5674082019-04-19 15:00:47 -04002343 dev->save();
2344 // We use clipRegion because it is already defined to operate in dev-space
2345 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2346 // but we don't want that, so we undo that before calling in.
Michael Ludwig915b7792019-10-22 17:40:41 +00002347 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002348 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002349 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002350 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002351 }
2352
Mike Reed38992392019-07-30 10:48:15 -04002353 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002354}
2355
reed41af9662015-01-05 07:49:08 -08002356void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002357 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002358 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002359 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002360 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002361 return;
2362 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002363 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002364
Mike Reed38992392019-07-30 10:48:15 -04002365 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002366
2367 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002368 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002369 }
2370
Mike Reed38992392019-07-30 10:48:15 -04002371 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002372}
2373
bsalomonac3aa242016-08-19 11:25:19 -07002374void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2375 SkScalar sweepAngle, bool useCenter,
2376 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002377 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002378 if (paint.canComputeFastBounds()) {
2379 SkRect storage;
2380 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002381 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002382 return;
2383 }
bsalomonac3aa242016-08-19 11:25:19 -07002384 }
2385
Mike Reed38992392019-07-30 10:48:15 -04002386 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002387
2388 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002389 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002390 }
2391
Mike Reed38992392019-07-30 10:48:15 -04002392 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002393}
2394
reed41af9662015-01-05 07:49:08 -08002395void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002396 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002397 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002398 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2399 return;
2400 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002401 }
2402
2403 if (rrect.isRect()) {
2404 // call the non-virtual version
2405 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002406 return;
2407 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002408 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002409 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2410 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002411 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002412
Mike Reed38992392019-07-30 10:48:15 -04002413 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002414
2415 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002416 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002417 }
2418
Mike Reed38992392019-07-30 10:48:15 -04002419 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002420}
2421
Mike Reed822128b2017-02-28 16:41:03 -05002422void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002423 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002424 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002425 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2426 return;
2427 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002428 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002429
Mike Reed38992392019-07-30 10:48:15 -04002430 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002431
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002432 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002433 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002434 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002435
Mike Reed38992392019-07-30 10:48:15 -04002436 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002437}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002438
reed41af9662015-01-05 07:49:08 -08002439void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002440 if (!path.isFinite()) {
2441 return;
2442 }
2443
Mike Reed822128b2017-02-28 16:41:03 -05002444 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002445 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002446 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002447 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2448 return;
2449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002451
Mike Reed822128b2017-02-28 16:41:03 -05002452 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002453 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002454 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002455 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002456 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002458
Mike Reed38992392019-07-30 10:48:15 -04002459 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460
2461 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002462 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002463 }
2464
Mike Reed38992392019-07-30 10:48:15 -04002465 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466}
2467
reed262a71b2015-12-05 13:07:27 -08002468bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002469 if (!paint.getImageFilter()) {
2470 return false;
2471 }
2472
2473 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002474 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002475 return false;
2476 }
2477
2478 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2479 // Once we can filter and the filter will return a result larger than itself, we should be
2480 // able to remove this constraint.
2481 // skbug.com/4526
2482 //
2483 SkPoint pt;
2484 ctm.mapXY(x, y, &pt);
2485 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2486 return ir.contains(fMCRec->fRasterClip.getBounds());
2487}
2488
Mike Reedf441cfc2018-04-11 14:50:16 -04002489// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2490// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2491// null.
2492static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2493 if (paintParam) {
2494 *real = *paintParam;
2495 real->setStyle(SkPaint::kFill_Style);
2496 real->setPathEffect(nullptr);
2497 paintParam = real;
2498 }
2499 return paintParam;
2500}
2501
reeda85d4d02015-05-06 12:56:48 -07002502void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002503 SkPaint realPaint;
2504 paint = init_image_paint(&realPaint, paint);
2505
reeda85d4d02015-05-06 12:56:48 -07002506 SkRect bounds = SkRect::MakeXYWH(x, y,
2507 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002508 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002509 SkRect tmp = bounds;
2510 if (paint) {
2511 paint->computeFastBounds(tmp, &tmp);
2512 }
2513 if (this->quickReject(tmp)) {
2514 return;
2515 }
reeda85d4d02015-05-06 12:56:48 -07002516 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002517 // At this point we need a real paint object. If the caller passed null, then we should
2518 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2519 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2520 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002521
reeda2217ef2016-07-20 06:04:34 -07002522 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002523 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2524 *paint);
2525 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002526 special = this->getDevice()->makeSpecial(image);
2527 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002528 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002529 }
2530 }
2531
Mike Reed38992392019-07-30 10:48:15 -04002532 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002533
reeda85d4d02015-05-06 12:56:48 -07002534 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002535 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002536 if (special) {
2537 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002538 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002539 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002540 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002541 SkScalarRoundToInt(pt.fY), pnt,
2542 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002543 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002544 iter.fDevice->drawImageRect(
2545 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2546 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002547 }
reeda85d4d02015-05-06 12:56:48 -07002548 }
halcanary9d524f22016-03-29 09:03:52 -07002549
Mike Reed38992392019-07-30 10:48:15 -04002550 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002551}
2552
reed41af9662015-01-05 07:49:08 -08002553void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002554 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002555 SkPaint realPaint;
2556 paint = init_image_paint(&realPaint, paint);
2557
halcanary96fcdcc2015-08-27 07:41:13 -07002558 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002559 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002560 if (paint) {
2561 paint->computeFastBounds(dst, &storage);
2562 }
2563 if (this->quickReject(storage)) {
2564 return;
2565 }
reeda85d4d02015-05-06 12:56:48 -07002566 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002567 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002568
Mike Reed38992392019-07-30 10:48:15 -04002569 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002570
reeda85d4d02015-05-06 12:56:48 -07002571 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002572 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002573 }
halcanary9d524f22016-03-29 09:03:52 -07002574
Mike Reed38992392019-07-30 10:48:15 -04002575 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002576}
2577
reed41af9662015-01-05 07:49:08 -08002578void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002579 SkDEBUGCODE(bitmap.validate();)
2580
reed33366972015-10-08 09:22:02 -07002581 if (bitmap.drawsNothing()) {
2582 return;
2583 }
2584
Mike Reedf441cfc2018-04-11 14:50:16 -04002585 SkPaint realPaint;
2586 init_image_paint(&realPaint, paint);
2587 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002588
Mike Reed822128b2017-02-28 16:41:03 -05002589 SkRect bounds;
2590 bitmap.getBounds(&bounds);
2591 bounds.offset(x, y);
2592 bool canFastBounds = paint->canComputeFastBounds();
2593 if (canFastBounds) {
2594 SkRect storage;
2595 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002596 return;
2597 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002598 }
reed@google.com4b226022011-01-11 18:32:13 +00002599
reeda2217ef2016-07-20 06:04:34 -07002600 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002601 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2602 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002603 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002604 special = this->getDevice()->makeSpecial(bitmap);
2605 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002606 drawAsSprite = false;
2607 }
2608 }
2609
Mike Reed38992392019-07-30 10:48:15 -04002610 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002611
2612 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002613 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002614 if (special) {
reed262a71b2015-12-05 13:07:27 -08002615 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002616 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002617 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002618 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002619 SkScalarRoundToInt(pt.fY), pnt,
2620 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002621 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002622 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2623 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2624 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002625 }
reed33366972015-10-08 09:22:02 -07002626 }
msarettfbfa2582016-08-12 08:29:08 -07002627
Mike Reed38992392019-07-30 10:48:15 -04002628 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002629}
2630
reed@google.com9987ec32011-09-07 11:57:52 +00002631// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002632void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002633 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002634 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002635 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002636 return;
2637 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002638
halcanary96fcdcc2015-08-27 07:41:13 -07002639 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002640 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002641 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2642 return;
2643 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002644 }
reed@google.com3d608122011-11-21 15:16:16 +00002645
reed@google.com33535f32012-09-25 15:37:50 +00002646 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002647 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002648 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002649 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002650
Mike Reed38992392019-07-30 10:48:15 -04002651 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002652
reed@google.com33535f32012-09-25 15:37:50 +00002653 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002654 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002655 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002656
Mike Reed38992392019-07-30 10:48:15 -04002657 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002658}
2659
reed41af9662015-01-05 07:49:08 -08002660void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002661 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002662 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002663 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002664}
2665
reed4c21dc52015-06-25 12:32:03 -07002666void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2667 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002668 SkPaint realPaint;
2669 paint = init_image_paint(&realPaint, paint);
2670
halcanary96fcdcc2015-08-27 07:41:13 -07002671 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002672 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002673 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2674 return;
2675 }
reed@google.com3d608122011-11-21 15:16:16 +00002676 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002677 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002678
Mike Reed38992392019-07-30 10:48:15 -04002679 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002680
reed4c21dc52015-06-25 12:32:03 -07002681 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002682 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002683 }
halcanary9d524f22016-03-29 09:03:52 -07002684
Mike Reed38992392019-07-30 10:48:15 -04002685 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002686}
2687
reed41af9662015-01-05 07:49:08 -08002688void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2689 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002690 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002691 SkPaint realPaint;
2692 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002693
halcanary96fcdcc2015-08-27 07:41:13 -07002694 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002695 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002696 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2697 return;
2698 }
reed4c21dc52015-06-25 12:32:03 -07002699 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002700 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002701
Mike Reed38992392019-07-30 10:48:15 -04002702 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002703
reed4c21dc52015-06-25 12:32:03 -07002704 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002705 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002706 }
halcanary9d524f22016-03-29 09:03:52 -07002707
Mike Reed38992392019-07-30 10:48:15 -04002708 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002709}
2710
msarett16882062016-08-16 09:31:08 -07002711void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2712 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002713 SkPaint realPaint;
2714 paint = init_image_paint(&realPaint, paint);
2715
msarett16882062016-08-16 09:31:08 -07002716 if (nullptr == paint || paint->canComputeFastBounds()) {
2717 SkRect storage;
2718 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2719 return;
2720 }
2721 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002722 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002723
Mike Reed38992392019-07-30 10:48:15 -04002724 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002725
2726 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002727 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002728 }
2729
Mike Reed38992392019-07-30 10:48:15 -04002730 DRAW_END
msarett16882062016-08-16 09:31:08 -07002731}
2732
2733void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2734 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002735 SkPaint realPaint;
2736 paint = init_image_paint(&realPaint, paint);
2737
msarett16882062016-08-16 09:31:08 -07002738 if (nullptr == paint || paint->canComputeFastBounds()) {
2739 SkRect storage;
2740 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2741 return;
2742 }
2743 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002744 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002745
Mike Reed38992392019-07-30 10:48:15 -04002746 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002747
2748 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002749 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002750 }
2751
Mike Reed38992392019-07-30 10:48:15 -04002752 DRAW_END
msarett16882062016-08-16 09:31:08 -07002753}
2754
fmalita00d5c2c2014-08-21 08:53:26 -07002755void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2756 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002757 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002758 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002759 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002760 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002761 SkRect tmp;
2762 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2763 return;
2764 }
2765 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002766 }
2767
fmalita024f9962015-03-03 19:08:17 -08002768 // We cannot filter in the looper as we normally do, because the paint is
2769 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002770 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002771
fmalitaaa1b9122014-08-28 14:32:24 -07002772 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002773 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002774 }
2775
Mike Reed38992392019-07-30 10:48:15 -04002776 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002777}
2778
Mike Reed358fcad2018-11-23 15:27:51 -05002779// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002780void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002781 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2782 TRACE_EVENT0("skia", TRACE_FUNC);
2783 if (byteLength) {
2784 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002785 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002786 }
2787}
Mike Reed4f81bb72019-01-23 09:23:00 -05002788
fmalita00d5c2c2014-08-21 08:53:26 -07002789void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2790 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002791 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002792 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002793 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002794 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002795}
reed@google.come0d9ce82014-04-23 04:00:17 +00002796
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002797void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002798 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002799 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002800
2801 while (iter.next()) {
2802 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002803 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002804 }
2805
Mike Reed38992392019-07-30 10:48:15 -04002806 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002807}
2808
dandovb3c9d1c2014-08-12 08:34:29 -07002809void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002810 const SkPoint texCoords[4], SkBlendMode bmode,
2811 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002812 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002813 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002814 return;
2815 }
mtklein6cfa73a2014-08-13 13:33:49 -07002816
Mike Reedfaba3712016-11-03 14:45:31 -04002817 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002818}
2819
2820void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002821 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002822 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002823 // Since a patch is always within the convex hull of the control points, we discard it when its
2824 // bounding rectangle is completely outside the current clip.
2825 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002826 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002827 if (this->quickReject(bounds)) {
2828 return;
2829 }
mtklein6cfa73a2014-08-13 13:33:49 -07002830
Mike Reed38992392019-07-30 10:48:15 -04002831 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002832
dandovecfff212014-08-04 10:02:00 -07002833 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002834 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002835 }
mtklein6cfa73a2014-08-13 13:33:49 -07002836
Mike Reed38992392019-07-30 10:48:15 -04002837 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002838}
2839
reeda8db7282015-07-07 10:22:31 -07002840void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002841#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002842 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002843#endif
reede3b38ce2016-01-08 09:18:44 -08002844 RETURN_ON_NULL(dr);
2845 if (x || y) {
2846 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2847 this->onDrawDrawable(dr, &matrix);
2848 } else {
2849 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002850 }
2851}
2852
reeda8db7282015-07-07 10:22:31 -07002853void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002854#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002855 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002856#endif
reede3b38ce2016-01-08 09:18:44 -08002857 RETURN_ON_NULL(dr);
2858 if (matrix && matrix->isIdentity()) {
2859 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002860 }
reede3b38ce2016-01-08 09:18:44 -08002861 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002862}
2863
2864void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002865 // drawable bounds are no longer reliable (e.g. android displaylist)
2866 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002867 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002868}
2869
reed71c3c762015-06-24 10:29:17 -07002870void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002871 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002872 const SkRect* cull, const SkPaint* paint) {
2873 if (cull && this->quickReject(*cull)) {
2874 return;
2875 }
2876
2877 SkPaint pnt;
2878 if (paint) {
2879 pnt = *paint;
2880 }
halcanary9d524f22016-03-29 09:03:52 -07002881
Mike Reed38992392019-07-30 10:48:15 -04002882 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002883 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002884 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002885 }
Mike Reed38992392019-07-30 10:48:15 -04002886 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002887}
2888
reedf70b5312016-03-04 16:36:20 -08002889void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2890 SkASSERT(key);
2891
2892 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002893 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002894 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002895 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002896 }
Mike Reed38992392019-07-30 10:48:15 -04002897 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002898}
2899
Michael Ludwiga595f862019-08-27 15:25:49 -04002900void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2901 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002902 SkASSERT(r.isSorted());
2903
2904 // If this used a paint, it would be a filled color with blend mode, which does not
2905 // need to use an autodraw loop, so use SkDrawIter directly.
2906 if (this->quickReject(r)) {
2907 return;
2908 }
2909
Michael Ludwiga4b44882019-08-28 14:34:58 -04002910 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002911 SkDrawIter iter(this);
2912 while(iter.next()) {
2913 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2914 }
2915}
2916
2917void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2918 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2919 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002920 if (count <= 0) {
2921 // Nothing to draw
2922 return;
2923 }
2924
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002925 SkPaint realPaint;
2926 init_image_paint(&realPaint, paint);
2927
Michael Ludwiga4b44882019-08-28 14:34:58 -04002928 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2929 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2930 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2931 // or we need it for the autolooper (since it greatly improves image filter perf).
2932 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2933 bool setBoundsValid = count == 1 || needsAutoLooper;
2934 SkRect setBounds = imageSet[0].fDstRect;
2935 if (imageSet[0].fMatrixIndex >= 0) {
2936 // Account for the per-entry transform that is applied prior to the CTM when drawing
2937 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002938 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002939 if (needsAutoLooper) {
2940 for (int i = 1; i < count; ++i) {
2941 SkRect entryBounds = imageSet[i].fDstRect;
2942 if (imageSet[i].fMatrixIndex >= 0) {
2943 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2944 }
2945 setBounds.joinPossiblyEmptyRect(entryBounds);
2946 }
2947 }
2948
2949 // If we happen to have the draw bounds, though, might as well check quickReject().
2950 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2951 SkRect tmp;
2952 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2953 return;
2954 }
2955 }
2956
2957 if (needsAutoLooper) {
2958 SkASSERT(setBoundsValid);
2959 DRAW_BEGIN(realPaint, &setBounds)
2960 while (iter.next()) {
2961 iter.fDevice->drawEdgeAAImageSet(
2962 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2963 }
2964 DRAW_END
2965 } else {
2966 this->predrawNotify();
2967 SkDrawIter iter(this);
2968 while(iter.next()) {
2969 iter.fDevice->drawEdgeAAImageSet(
2970 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2971 }
2972 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002973}
2974
reed@android.com8a1c16f2008-12-17 15:59:43 +00002975//////////////////////////////////////////////////////////////////////////////
2976// These methods are NOT virtual, and therefore must call back into virtual
2977// methods, rather than actually drawing themselves.
2978//////////////////////////////////////////////////////////////////////////////
2979
reed374772b2016-10-05 17:33:02 -07002980void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002981 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002982 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002983 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002984 this->drawPaint(paint);
2985}
2986
2987void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002988 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002989 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2990}
2991
Mike Reed3661bc92017-02-22 13:21:42 -05002992void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002993 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002994 pts[0].set(x0, y0);
2995 pts[1].set(x1, y1);
2996 this->drawPoints(kLines_PointMode, 2, pts, paint);
2997}
2998
Mike Reed3661bc92017-02-22 13:21:42 -05002999void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000 if (radius < 0) {
3001 radius = 0;
3002 }
3003
3004 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04003005 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003006 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003007}
3008
3009void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3010 const SkPaint& paint) {
3011 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003012 SkRRect rrect;
3013 rrect.setRectXY(r, rx, ry);
3014 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003015 } else {
3016 this->drawRect(r, paint);
3017 }
3018}
3019
reed@android.com8a1c16f2008-12-17 15:59:43 +00003020void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3021 SkScalar sweepAngle, bool useCenter,
3022 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04003023 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07003024 if (oval.isEmpty() || !sweepAngle) {
3025 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003026 }
bsalomon21af9ca2016-08-25 12:29:23 -07003027 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003028}
3029
reed@android.comf76bacf2009-05-13 14:00:33 +00003030///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003031#ifdef SK_DISABLE_SKPICTURE
3032void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07003033
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003034
3035void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3036 const SkPaint* paint) {}
3037#else
Mike Klein88d90712018-01-27 17:30:04 +00003038/**
3039 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3040 * against the playback cost of recursing into the subpicture to get at its actual ops.
3041 *
3042 * For now we pick a conservatively small value, though measurement (and other heuristics like
3043 * the type of ops contained) may justify changing this value.
3044 */
3045#define kMaxPictureOpsToUnrollInsteadOfRef 1
3046
reedd5fa1a42014-08-09 11:08:05 -07003047void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04003048 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08003049 RETURN_ON_NULL(picture);
3050
reede3b38ce2016-01-08 09:18:44 -08003051 if (matrix && matrix->isIdentity()) {
3052 matrix = nullptr;
3053 }
Mike Klein88d90712018-01-27 17:30:04 +00003054 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3055 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3056 picture->playback(this);
3057 } else {
3058 this->onDrawPicture(picture, matrix, paint);
3059 }
reedd5fa1a42014-08-09 11:08:05 -07003060}
robertphillips9b14f262014-06-04 05:40:44 -07003061
reedd5fa1a42014-08-09 11:08:05 -07003062void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3063 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003064 if (!paint || paint->canComputeFastBounds()) {
3065 SkRect bounds = picture->cullRect();
3066 if (paint) {
3067 paint->computeFastBounds(bounds, &bounds);
3068 }
3069 if (matrix) {
3070 matrix->mapRect(&bounds);
3071 }
3072 if (this->quickReject(bounds)) {
3073 return;
3074 }
3075 }
3076
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003077 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003078 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003079}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003080#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003081
reed@android.com8a1c16f2008-12-17 15:59:43 +00003082///////////////////////////////////////////////////////////////////////////////
3083///////////////////////////////////////////////////////////////////////////////
3084
reed3aafe112016-08-18 12:45:34 -07003085SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003086 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003087
3088 SkASSERT(canvas);
3089
reed3aafe112016-08-18 12:45:34 -07003090 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003091 fDone = !fImpl->next();
3092}
3093
3094SkCanvas::LayerIter::~LayerIter() {
3095 fImpl->~SkDrawIter();
3096}
3097
3098void SkCanvas::LayerIter::next() {
3099 fDone = !fImpl->next();
3100}
3101
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003102SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003103 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003104}
3105
3106const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003107 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003108}
3109
3110const SkPaint& SkCanvas::LayerIter::paint() const {
3111 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003112 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003113 paint = &fDefaultPaint;
3114 }
3115 return *paint;
3116}
3117
Mike Reedca37f322018-03-08 13:22:16 -05003118SkIRect SkCanvas::LayerIter::clipBounds() const {
3119 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003120}
3121
Michael Ludwig915b7792019-10-22 17:40:41 +00003122int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3123int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003124
3125///////////////////////////////////////////////////////////////////////////////
3126
Brian Osmane8a98632019-04-10 10:26:10 -04003127SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3128SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3129SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3130SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3131
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003132SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3133 const SkRect& dstRect, int matrixIndex, float alpha,
3134 unsigned aaFlags, bool hasClip)
3135 : fImage(std::move(image))
3136 , fSrcRect(srcRect)
3137 , fDstRect(dstRect)
3138 , fMatrixIndex(matrixIndex)
3139 , fAlpha(alpha)
3140 , fAAFlags(aaFlags)
3141 , fHasClip(hasClip) {}
3142
3143SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3144 const SkRect& dstRect, float alpha, unsigned aaFlags)
3145 : fImage(std::move(image))
3146 , fSrcRect(srcRect)
3147 , fDstRect(dstRect)
3148 , fAlpha(alpha)
3149 , fAAFlags(aaFlags) {}
3150
3151///////////////////////////////////////////////////////////////////////////////
3152
Mike Reed5df49342016-11-12 08:06:55 -06003153std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003154 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003155 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003156 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003157 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003158
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003159 SkBitmap bitmap;
3160 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003161 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003162 }
Mike Reed12f77342017-11-08 11:19:52 -05003163
3164 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003165 std::make_unique<SkCanvas>(bitmap, *props) :
3166 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003167}
reedd5fa1a42014-08-09 11:08:05 -07003168
3169///////////////////////////////////////////////////////////////////////////////
3170
Florin Malitaee424ac2016-12-01 12:47:59 -05003171SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003172 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003173
Florin Malita439ace92016-12-02 12:05:41 -05003174SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003175 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003176
Herb Derbyefe39bc2018-05-01 17:06:20 -04003177SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003178 : INHERITED(device) {}
3179
Florin Malitaee424ac2016-12-01 12:47:59 -05003180SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3181 (void)this->INHERITED::getSaveLayerStrategy(rec);
3182 return kNoLayer_SaveLayerStrategy;
3183}
3184
Mike Reed148b7fd2018-12-18 17:38:18 -05003185bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3186 return false;
3187}
3188
Florin Malitaee424ac2016-12-01 12:47:59 -05003189///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003190
reed73603f32016-09-20 08:42:38 -07003191static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3192static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3193static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3194static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3195static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3196static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003197
3198///////////////////////////////////////////////////////////////////////////////////////////////////
3199
3200SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3201 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003202 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003203 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwig915b7792019-10-22 17:40:41 +00003204 SkIPoint origin = dev->getOrigin();
3205 SkMatrix ctm = this->getTotalMatrix();
3206 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3207
3208 SkIRect clip = fMCRec->fRasterClip.getBounds();
3209 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003210 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003211 clip.setEmpty();
3212 }
3213
Michael Ludwig915b7792019-10-22 17:40:41 +00003214 fAllocator->updateHandle(handle, ctm, clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003215 return handle;
3216 }
3217 return nullptr;
3218}
3219
3220static bool install(SkBitmap* bm, const SkImageInfo& info,
3221 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003222 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003223}
3224
3225SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3226 SkBitmap* bm) {
3227 SkRasterHandleAllocator::Rec rec;
3228 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3229 return nullptr;
3230 }
3231 return rec.fHandle;
3232}
3233
3234std::unique_ptr<SkCanvas>
3235SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3236 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003237 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003238 return nullptr;
3239 }
3240
3241 SkBitmap bm;
3242 Handle hndl;
3243
3244 if (rec) {
3245 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3246 } else {
3247 hndl = alloc->allocBitmap(info, &bm);
3248 }
3249 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3250}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003251
3252///////////////////////////////////////////////////////////////////////////////////////////////////