blob: e0310f8a0e60c2e6f5590b5bfd221b1d390f5369 [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 Kleinc0bd9f92019-04-23 12:05:21 -050035#include "src/core/SkMatrixUtils.h"
36#include "src/core/SkPaintPriv.h"
37#include "src/core/SkRasterClip.h"
38#include "src/core/SkSpecialImage.h"
39#include "src/core/SkStrikeCache.h"
40#include "src/core/SkTLazy.h"
41#include "src/core/SkTextFormatParams.h"
42#include "src/core/SkTraceEvent.h"
43#include "src/image/SkImage_Base.h"
44#include "src/image/SkSurface_Base.h"
45#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040046
bungemand3ebb482015-08-05 13:57:49 -070047#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000048
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000049#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050050#include "include/gpu/GrContext.h"
51#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#endif
53
reede3b38ce2016-01-08 09:18:44 -080054#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050055#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080056
Mike Klein1bb7e232019-12-10 08:58:52 -060057// This is a test: static_assert with no message is a c++17 feature,
58// and std::max() is constexpr only since the c++14 stdlib.
59static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000060
Mike Reed139e5e02017-03-08 11:29:33 -050061///////////////////////////////////////////////////////////////////////////////////////////////////
62
reedc83a2972015-07-16 07:40:45 -070063/*
64 * Return true if the drawing this rect would hit every pixels in the canvas.
65 *
66 * Returns false if
67 * - rect does not contain the canvas' bounds
68 * - paint is not fill
69 * - paint would blur or otherwise change the coverage of the rect
70 */
71bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
72 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070073 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
74 (int)kNone_ShaderOverrideOpacity,
75 "need_matching_enums0");
76 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
77 (int)kOpaque_ShaderOverrideOpacity,
78 "need_matching_enums1");
79 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
80 (int)kNotOpaque_ShaderOverrideOpacity,
81 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070082
83 const SkISize size = this->getBaseLayerSize();
84 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050085
86 // if we're clipped at all, we can't overwrite the entire surface
87 {
88 SkBaseDevice* base = this->getDevice();
89 SkBaseDevice* top = this->getTopDevice();
90 if (base != top) {
91 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
92 }
93 if (!base->clipIsWideOpen()) {
94 return false;
95 }
reedc83a2972015-07-16 07:40:45 -070096 }
97
98 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070099 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700100 return false; // conservative
101 }
halcanaryc5769b22016-08-10 07:13:21 -0700102
103 SkRect devRect;
104 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
105 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700106 return false;
107 }
108 }
109
110 if (paint) {
111 SkPaint::Style paintStyle = paint->getStyle();
112 if (!(paintStyle == SkPaint::kFill_Style ||
113 paintStyle == SkPaint::kStrokeAndFill_Style)) {
114 return false;
115 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400116 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700117 return false; // conservative
118 }
119 }
120 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
121}
122
123///////////////////////////////////////////////////////////////////////////////////////////////////
124
reed@google.comda17f752012-08-16 18:27:05 +0000125// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126//#define SK_TRACE_SAVERESTORE
127
128#ifdef SK_TRACE_SAVERESTORE
129 static int gLayerCounter;
130 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
131 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
132
133 static int gRecCounter;
134 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
135 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
136
137 static int gCanvasCounter;
138 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
139 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
140#else
141 #define inc_layer()
142 #define dec_layer()
143 #define inc_rec()
144 #define dec_rec()
145 #define inc_canvas()
146 #define dec_canvas()
147#endif
148
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000149typedef SkTLazy<SkPaint> SkLazyPaint;
150
reedc83a2972015-07-16 07:40:45 -0700151void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000152 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700153 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
154 ? SkSurface::kDiscard_ContentChangeMode
155 : SkSurface::kRetain_ContentChangeMode);
156 }
157}
158
159void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
160 ShaderOverrideOpacity overrideOpacity) {
161 if (fSurfaceBase) {
162 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
163 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
164 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
165 // and therefore we don't care which mode we're in.
166 //
167 if (fSurfaceBase->outstandingImageSnapshot()) {
168 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
169 mode = SkSurface::kDiscard_ContentChangeMode;
170 }
171 }
172 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000173 }
174}
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000178/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 The clip/matrix/proc are fields that reflect the top of the save/restore
180 stack. Whenever the canvas changes, it marks a dirty flag, and then before
181 these are used (assuming we're not on a layer) we rebuild these cache
182 values: they reflect the top of the save stack, but translated and clipped
183 by the device's XY offset and bitmap-bounds.
184*/
185struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400186 DeviceCM* fNext;
187 sk_sp<SkBaseDevice> fDevice;
188 SkRasterClip fClip;
189 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
190 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400191 sk_sp<SkImage> fClipImage;
192 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
Florin Malita53f77bd2017-04-28 13:48:37 -0400194 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000195 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700196 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400197 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500198 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700199 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000200 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400201 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400202 {}
reed@google.com4b226022011-01-11 18:32:13 +0000203
mtkleinfeaadee2015-04-08 11:25:48 -0700204 void reset(const SkIRect& bounds) {
205 SkASSERT(!fPaint);
206 SkASSERT(!fNext);
207 SkASSERT(fDevice);
208 fClip.setRect(bounds);
209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210};
211
Mike Reed148b7fd2018-12-18 17:38:18 -0500212namespace {
213// Encapsulate state needed to restore from saveBehind()
214struct BackImage {
215 sk_sp<SkSpecialImage> fImage;
216 SkIPoint fLoc;
217};
218}
219
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220/* This is the record we keep for each save/restore level in the stack.
221 Since a level optionally copies the matrix and/or stack, we have pointers
222 for these fields. If the value is copied for this level, the copy is
223 stored in the ...Storage field, and the pointer points to that. If the
224 value is not copied for this level, we ignore ...Storage, and just point
225 at the corresponding value in the previous level in the stack.
226*/
227class SkCanvas::MCRec {
228public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500229 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 /* If there are any layers in the stack, this points to the top-most
231 one that is at or below this level in the stack (so we know what
232 bitmap/device to draw into from this level. This value is NOT
233 reference counted, since the real owner is either our fLayer field,
234 or a previous one in a lower level.)
235 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500236 DeviceCM* fTopLayer;
237 std::unique_ptr<BackImage> fBackImage;
238 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500239 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500240 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241
Mike Reeda1361362017-03-07 09:37:29 -0500242 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700243 fLayer = nullptr;
244 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800245 fMatrix.reset();
246 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700247
reedd9544982014-09-09 18:46:22 -0700248 // don't bother initializing fNext
249 inc_rec();
250 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400251 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700252 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700253 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800254 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 // don't bother initializing fNext
257 inc_rec();
258 }
259 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700260 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 dec_rec();
262 }
mtkleinfeaadee2015-04-08 11:25:48 -0700263
264 void reset(const SkIRect& bounds) {
265 SkASSERT(fLayer);
266 SkASSERT(fDeferredSaveCount == 0);
267
268 fMatrix.reset();
269 fRasterClip.setRect(bounds);
270 fLayer->reset(bounds);
271 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
Mike Reeda1361362017-03-07 09:37:29 -0500274class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275public:
Mike Reeda1361362017-03-07 09:37:29 -0500276 SkDrawIter(SkCanvas* canvas)
277 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
278 {}
reed@google.com4b226022011-01-11 18:32:13 +0000279
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000281 const DeviceCM* rec = fCurrLayer;
282 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400283 fDevice = rec->fDevice.get();
284 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700286 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 return true;
288 }
289 return false;
290 }
reed@google.com4b226022011-01-11 18:32:13 +0000291
Michael Ludwig915b7792019-10-22 17:40:41 +0000292 int getX() const { return fDevice->getOrigin().x(); }
293 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000295
Mike Reed99330ba2017-02-22 11:01:08 -0500296 SkBaseDevice* fDevice;
297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 const DeviceCM* fCurrLayer;
300 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301};
302
Florin Malita713b8ef2017-04-28 10:57:24 -0400303#define FOR_EACH_TOP_DEVICE( code ) \
304 do { \
305 DeviceCM* layer = fMCRec->fTopLayer; \
306 while (layer) { \
307 SkBaseDevice* device = layer->fDevice.get(); \
308 if (device) { \
309 code; \
310 } \
311 layer = layer->fNext; \
312 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500313 } while (0)
314
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315/////////////////////////////////////////////////////////////////////////////
316
reeddbc3cef2015-04-29 12:18:57 -0700317/**
318 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700319 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700320 */
reedd053ce92016-03-22 10:17:23 -0700321static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700322 SkImageFilter* imgf = paint.getImageFilter();
323 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700324 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700325 }
326
reedd053ce92016-03-22 10:17:23 -0700327 SkColorFilter* imgCFPtr;
328 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700329 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700330 }
reedd053ce92016-03-22 10:17:23 -0700331 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700332
333 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700334 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700335 // there is no existing paint colorfilter, so we can just return the imagefilter's
336 return imgCF;
337 }
338
339 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
340 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500341 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700342}
343
senorblanco87e066e2015-10-28 11:23:36 -0700344/**
345 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
346 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
347 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
348 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
349 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
350 * conservative "effective" bounds based on the settings in the paint... with one exception. This
351 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
352 * deliberately ignored.
353 */
354static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
355 const SkRect& rawBounds,
356 SkRect* storage) {
357 SkPaint tmpUnfiltered(paint);
358 tmpUnfiltered.setImageFilter(nullptr);
359 if (tmpUnfiltered.canComputeFastBounds()) {
360 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
361 } else {
362 return rawBounds;
363 }
364}
365
Mike Reed38992392019-07-30 10:48:15 -0400366class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367public:
senorblanco87e066e2015-10-28 11:23:36 -0700368 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
369 // paint. It's used to determine the size of the offscreen layer for filters.
370 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400371 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
372 bool skipLayerForImageFilter = false,
373 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400375 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700377 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378
Mike Reed38992392019-07-30 10:48:15 -0400379 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
380 SkASSERT(!fLazyPaint.isValid());
381 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700382 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700383 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700384 fPaint = paint;
385 }
386
387 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700388 /**
389 * We implement ImageFilters for a given draw by creating a layer, then applying the
390 * imagefilter to the pixels of that layer (its backing surface/image), and then
391 * we call restore() to xfer that layer to the main canvas.
392 *
393 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
394 * 2. Generate the src pixels:
395 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
396 * return (fPaint). We then draw the primitive (using srcover) into a cleared
397 * buffer/surface.
398 * 3. Restore the layer created in #1
399 * The imagefilter is passed the buffer/surface from the layer (now filled with the
400 * src pixels of the primitive). It returns a new "filtered" buffer, which we
401 * draw onto the previous layer using the xfermode from the original paint.
402 */
Mike Reed38992392019-07-30 10:48:15 -0400403
404 SkPaint restorePaint;
405 restorePaint.setImageFilter(fPaint->refImageFilter());
406 restorePaint.setBlendMode(fPaint->getBlendMode());
407
senorblanco87e066e2015-10-28 11:23:36 -0700408 SkRect storage;
409 if (rawBounds) {
410 // Make rawBounds include all paint outsets except for those due to image filters.
411 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
412 }
Mike Reed38992392019-07-30 10:48:15 -0400413 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700414 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700415 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000416
Mike Reed38992392019-07-30 10:48:15 -0400417 // Remove the restorePaint fields from our "working" paint
418 SkASSERT(!fLazyPaint.isValid());
419 SkPaint* paint = fLazyPaint.set(origPaint);
420 paint->setImageFilter(nullptr);
421 paint->setBlendMode(SkBlendMode::kSrcOver);
422 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423 }
424 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000425
Mike Reed38992392019-07-30 10:48:15 -0400426 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700427 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000428 fCanvas->internalRestore();
429 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000430 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000432
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433 const SkPaint& paint() const {
434 SkASSERT(fPaint);
435 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438private:
Mike Reed38992392019-07-30 10:48:15 -0400439 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000440 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400441 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000442 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700443 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444};
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446////////// macros to place around the internal draw calls //////////////////
447
Mike Reed38992392019-07-30 10:48:15 -0400448#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700449 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400450 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
451 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800452
453
Mike Reed38992392019-07-30 10:48:15 -0400454#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000455 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400456 AutoLayerForImageFilter draw(this, paint, true); \
457 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000458
Mike Reed38992392019-07-30 10:48:15 -0400459#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000460 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400461 AutoLayerForImageFilter draw(this, paint, false, bounds); \
462 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000463
Mike Reed38992392019-07-30 10:48:15 -0400464#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700465 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400466 AutoLayerForImageFilter draw(this, paint, false, bounds); \
467 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700468
Mike Reed38992392019-07-30 10:48:15 -0400469#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470
471////////////////////////////////////////////////////////////////////////////
472
msarettfbfa2582016-08-12 08:29:08 -0700473static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
474 if (bounds.isEmpty()) {
475 return SkRect::MakeEmpty();
476 }
477
478 // Expand bounds out by 1 in case we are anti-aliasing. We store the
479 // bounds as floats to enable a faster quick reject implementation.
480 SkRect dst;
481 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
482 return dst;
483}
484
mtkleinfeaadee2015-04-08 11:25:48 -0700485void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
486 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700487 fMCRec->reset(bounds);
488
489 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500490 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400491 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700492 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700493 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700494}
495
Hal Canary363a3f82018-10-04 11:04:48 -0400496void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800497 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500500 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500501 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700502 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503
reeda499f902015-05-01 09:34:31 -0700504 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
505 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400506 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509
halcanary96fcdcc2015-08-27 07:41:13 -0700510 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000511
reedf92c8662014-08-18 08:02:43 -0700512 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700513 // The root device and the canvas should always have the same pixel geometry
514 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800515 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700516 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700519 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400520
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500521 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
reed@google.comcde92112011-07-06 20:00:52 +0000524SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700526 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000527{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000528 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000529
Hal Canary363a3f82018-10-04 11:04:48 -0400530 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000531}
532
reed96a857e2015-01-25 10:33:58 -0800533SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800535 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000536{
537 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400538 this->init(sk_make_sp<SkNoPixelsDevice>(
Hal Canary363a3f82018-10-04 11:04:48 -0400539 SkIRect::MakeWH(SkTMax(width, 0), SkTMax(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700540}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000541
Hal Canary363a3f82018-10-04 11:04:48 -0400542SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700543 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700544 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700545{
546 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700547
Mike Reed566e53c2017-03-10 10:49:45 -0500548 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400549 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700550}
551
Herb Derbyefe39bc2018-05-01 17:06:20 -0400552SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000553 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700554 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000555{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700557
Hal Canary363a3f82018-10-04 11:04:48 -0400558 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700559}
560
reed4a8126e2014-09-22 07:29:03 -0700561SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700562 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700563 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700564{
565 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700566
Mike Reed910ca0f2018-04-25 13:04:05 -0400567 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400568 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700569}
reed29c857d2014-09-21 10:25:07 -0700570
Mike Reed356f7c22017-01-10 11:58:39 -0500571SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
572 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700573 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
574 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500575 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700576{
577 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700578
Mike Reed910ca0f2018-04-25 13:04:05 -0400579 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400580 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581}
582
Mike Reed356f7c22017-01-10 11:58:39 -0500583SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
584
Matt Sarett31f99ce2017-04-11 08:46:01 -0400585#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
586SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
587 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
588 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
589 , fAllocator(nullptr)
590{
591 inc_canvas();
592
593 SkBitmap tmp(bitmap);
594 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400595 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400596 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400597}
598#endif
599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600SkCanvas::~SkCanvas() {
601 // free up the contents of our deque
602 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 this->internalRestore(); // restore the last, since we're going away
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 dec_canvas();
607}
608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609///////////////////////////////////////////////////////////////////////////////
610
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000611void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700612 this->onFlush();
613}
614
615void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000616 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000617 if (device) {
618 device->flush();
619 }
620}
621
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500622SkSurface* SkCanvas::getSurface() const {
623 return fSurfaceBase;
624}
625
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000626SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000627 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000628 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
629}
630
senorblancoafc7cce2016-02-02 18:44:15 -0800631SkIRect SkCanvas::getTopLayerBounds() const {
632 SkBaseDevice* d = this->getTopDevice();
633 if (!d) {
634 return SkIRect::MakeEmpty();
635 }
Michael Ludwig915b7792019-10-22 17:40:41 +0000636 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
senorblancoafc7cce2016-02-02 18:44:15 -0800637}
638
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000639SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000641 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400643 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644}
645
Florin Malita0ed3b642017-01-13 16:56:38 +0000646SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400647 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000648}
649
Mike Reed353196f2017-07-21 11:01:18 -0400650bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000651 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400652 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000653}
654
Mike Reed353196f2017-07-21 11:01:18 -0400655bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
656 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400657}
658
659bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
660 SkPixmap pm;
661 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
662}
663
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000664bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400665 SkPixmap pm;
666 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700667 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000668 }
669 return false;
670}
671
Matt Sarett03dd6d52017-01-23 12:15:09 -0500672bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 SkBaseDevice* device = this->getDevice();
675 if (!device) {
676 return false;
677 }
678
Matt Sarett03dd6d52017-01-23 12:15:09 -0500679 // This check gives us an early out and prevents generation ID churn on the surface.
680 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
681 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400682 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500683 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685
Matt Sarett03dd6d52017-01-23 12:15:09 -0500686 // Tell our owning surface to bump its generation ID.
687 const bool completeOverwrite =
688 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700689 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700690
Matt Sarett03dd6d52017-01-23 12:15:09 -0500691 // This can still fail, most notably in the case of a invalid color type or alpha type
692 // conversion. We could pull those checks into this function and avoid the unnecessary
693 // generation ID bump. But then we would be performing those checks twice, since they
694 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400695 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000696}
reed@google.com51df9e32010-12-23 19:29:18 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698//////////////////////////////////////////////////////////////////////////////
699
reed2ff1fce2014-12-11 07:07:37 -0800700void SkCanvas::checkForDeferredSave() {
701 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800702 this->doSave();
703 }
704}
705
reedf0090cb2014-11-26 08:55:51 -0800706int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800707#ifdef SK_DEBUG
708 int count = 0;
709 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
710 for (;;) {
711 const MCRec* rec = (const MCRec*)iter.next();
712 if (!rec) {
713 break;
714 }
715 count += 1 + rec->fDeferredSaveCount;
716 }
717 SkASSERT(count == fSaveCount);
718#endif
719 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800720}
721
722int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800723 fSaveCount += 1;
724 fMCRec->fDeferredSaveCount += 1;
725 return this->getSaveCount() - 1; // return our prev value
726}
727
728void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800729 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700730
731 SkASSERT(fMCRec->fDeferredSaveCount > 0);
732 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800733 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800734}
735
Mike Reedc43f2a02020-01-16 14:54:34 -0500736int SkCanvas::experimental_saveCamera(const SkMatrix44& projection, const SkMatrix44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500737 // TODO: add a virtual for this, and update clients (e.g. chrome)
738 int n = this->save();
Mike Reedd4d3b332020-01-16 16:34:34 -0500739 this->experimental_concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500740 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500741 return n;
742}
743
reedf0090cb2014-11-26 08:55:51 -0800744void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800745 if (fMCRec->fDeferredSaveCount > 0) {
746 SkASSERT(fSaveCount > 1);
747 fSaveCount -= 1;
748 fMCRec->fDeferredSaveCount -= 1;
749 } else {
750 // check for underflow
751 if (fMCStack.count() > 1) {
752 this->willRestore();
753 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700754 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800755 this->internalRestore();
756 this->didRestore();
757 }
reedf0090cb2014-11-26 08:55:51 -0800758 }
759}
760
761void SkCanvas::restoreToCount(int count) {
762 // sanity check
763 if (count < 1) {
764 count = 1;
765 }
mtkleinf0f14112014-12-12 08:46:25 -0800766
reedf0090cb2014-11-26 08:55:51 -0800767 int n = this->getSaveCount() - count;
768 for (int i = 0; i < n; ++i) {
769 this->restore();
770 }
771}
772
reed2ff1fce2014-12-11 07:07:37 -0800773void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700775 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000777
Mike Reedc42a1cd2017-02-14 14:25:14 -0500778 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779}
780
reed4960eee2015-12-18 07:09:18 -0800781bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400782 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783}
784
reed4960eee2015-12-18 07:09:18 -0800785bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700786 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400787 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
788 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
789 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
790 // filter does not require a decomposed CTM matrix, the filter space and device space are the
791 // same. When it has been decomposed, we want the original image filter node to process the
792 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
793 // longer the original filter, but has the remainder matrix baked into it, and passing in the
794 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
795 // to the original filter node (barring inflation from consecutive calls to mapRect). While
796 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
797 // passing getDeviceClipBounds() to 'imageFilter' is correct.
798 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
799 // be important to more accurately calculate the clip bounds in the layer space for the original
800 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500801 SkIRect clipBounds = this->getDeviceClipBounds();
802 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000803 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000804 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000805
reed96e657d2015-03-10 17:30:07 -0700806 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
807
Robert Phillips12078432018-05-17 11:17:39 -0400808 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
809 // If the image filter DAG affects transparent black then we will need to render
810 // out to the clip bounds
811 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000812 }
Robert Phillips12078432018-05-17 11:17:39 -0400813
814 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700815 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700817 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400818 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400820 inputSaveLayerBounds = clipBounds;
821 }
822
823 if (imageFilter) {
824 // expand the clip bounds by the image filter DAG to include extra content that might
825 // be required by the image filters.
826 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
827 SkImageFilter::kReverse_MapDirection,
828 &inputSaveLayerBounds);
829 }
830
831 SkIRect clippedSaveLayerBounds;
832 if (bounds) {
833 // For better or for worse, user bounds currently act as a hard clip on the layer's
834 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
835 clippedSaveLayerBounds = inputSaveLayerBounds;
836 } else {
837 // If there are no user bounds, we don't want to artificially restrict the resulting
838 // layer bounds, so allow the expanded clip bounds free reign.
839 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800841
842 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400843 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800844 if (BoundsAffectsClip(saveLayerFlags)) {
845 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
846 fMCRec->fRasterClip.setEmpty();
847 fDeviceClipBounds.setEmpty();
848 }
849 return false;
850 }
Robert Phillips12078432018-05-17 11:17:39 -0400851 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852
reed4960eee2015-12-18 07:09:18 -0800853 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700854 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400855 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
856 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000857 }
858
859 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400860 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000861 }
Robert Phillips12078432018-05-17 11:17:39 -0400862
junov@chromium.orga907ac32012-02-24 21:54:07 +0000863 return true;
864}
865
reed4960eee2015-12-18 07:09:18 -0800866int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
867 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000868}
869
Cary Clarke041e312018-03-06 13:00:52 -0500870int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700871 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400872 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
873 // no need for the layer (or any of the draws until the matching restore()
874 this->save();
875 this->clipRect({0,0,0,0});
876 } else {
877 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
878 fSaveCount += 1;
879 this->internalSaveLayer(rec, strategy);
880 }
reed4960eee2015-12-18 07:09:18 -0800881 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800882}
883
Mike Reed148b7fd2018-12-18 17:38:18 -0500884int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
885 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
886 // Assuming clips never expand, if the request bounds is outside of the current clip
887 // there is no need to copy/restore the area, so just devolve back to a regular save.
888 this->save();
889 } else {
890 bool doTheWork = this->onDoSaveBehind(bounds);
891 fSaveCount += 1;
892 this->internalSave();
893 if (doTheWork) {
894 this->internalSaveBehind(bounds);
895 }
896 }
897 return this->getSaveCount() - 1;
898}
899
reeda2217ef2016-07-20 06:04:34 -0700900void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500901 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500902 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400903 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
904 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400905 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig915b7792019-10-22 17:40:41 +0000906
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400907 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400908 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
909 // This means that we only have to copy a dst-sized block of pixels out of src and translate
910 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400911 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
912 dstOrigin.y() - src->getOrigin().y(),
913 dst->width(), dst->height());
914 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400915 return;
916 }
917
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400918 auto special = src->snapSpecial(snapBounds);
919 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400920 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
921 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400922 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
923 }
924 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400925 }
926
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400927 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
928 // by the backdrop filter.
929 SkMatrix toRoot, layerMatrix;
930 SkSize scale;
931 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
932 toRoot = SkMatrix::I();
933 layerMatrix = ctm;
934 } else if (ctm.decomposeScale(&scale, &toRoot)) {
935 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
936 } else {
937 // Perspective, for now, do no scaling of the layer itself.
938 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
939 // the matrix, e.g. based on the midpoint of the near/far planes?
940 toRoot = ctm;
941 layerMatrix = SkMatrix::I();
942 }
943
944 // We have to map the dst bounds from the root space into the layer space where filtering will
945 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
946 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
947 // is a safe, conservative estimate.
948 SkMatrix fromRoot;
949 if (!toRoot.invert(&fromRoot)) {
950 return;
951 }
952
953 // This represents what the backdrop filter needs to produce in the layer space, and is sized
954 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
955 SkIRect layerTargetBounds = fromRoot.mapRect(
956 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
957 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
958 // require some extra input pixels.
959 SkIRect layerInputBounds = filter->filterBounds(
960 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
961 &layerTargetBounds);
962
963 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400964 // be the conservative contents required to fill a layerInputBounds-sized surface with the
965 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400966 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
967 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
968 if (!backdropBounds.intersect(srcDevRect)) {
969 return;
970 }
971
972 auto special = src->snapSpecial(backdropBounds);
973 if (!special) {
974 return;
975 }
976
977 SkColorType colorType = src->imageInfo().colorType();
978 if (colorType == kUnknown_SkColorType) {
979 colorType = kRGBA_8888_SkColorType;
980 }
981 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400982
983 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400984 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400985 // Drawing the temporary and final filtered image requires a higher filter quality if the
986 // 'toRoot' transformation is not identity, in order to minimize the impact on already
987 // rendered edges/content.
988 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
989 p.setFilterQuality(kHigh_SkFilterQuality);
990
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400991 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
992 // and stored in a temporary surface, which is then used as the input to the actual filter.
993 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
994 if (!tmpSurface) {
995 return;
996 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400997
998 auto tmpCanvas = tmpSurface->getCanvas();
999 tmpCanvas->clear(SK_ColorTRANSPARENT);
1000 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1001 // space, then maps from root space into the layer space, then maps it so the input layer's
1002 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1003 // performed on backdropBounds.
1004 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1005 tmpCanvas->concat(fromRoot);
1006 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001007
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001008 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1009 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1010 special = tmpSurface->makeImageSnapshot();
1011 } else {
1012 // Since there is no extra transform that was done, update the input bounds to reflect
1013 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1014 // was equal to backdropBounds before it was made relative to the src device and cropped.
1015 // When we use the original snapped image directly, just map the update backdrop bounds
1016 // back into the shared layer space
1017 layerInputBounds = backdropBounds;
1018 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001019
1020 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1021 // draw will be 1-1 so there is no need to increase filter quality.
1022 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001023 }
1024
1025 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1026 // layer space. This has to further offset everything so that filter evaluation thinks the
1027 // source image's top left corner is (0, 0).
1028 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1029 // this can be simplified.
1030 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1031 SkMatrix filterCTM = layerMatrix;
1032 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1033 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1034
1035 SkIPoint offset;
1036 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001037 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001038 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1039 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1040 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1041 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001042 offset += layerInputBounds.topLeft();
1043
1044 // Manually setting the device's CTM requires accounting for the device's origin.
1045 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001046 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001047 // a global CTM instead of a device CTM.
1048 SkMatrix dstCTM = toRoot;
1049 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001050 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001051
1052 // And because devices don't have a special-image draw function that supports arbitrary
1053 // matrices, we are abusing the asImage() functionality here...
1054 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001055 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001056 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001057 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001058 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1059 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001060 }
robertphillips7354a4b2015-12-16 05:08:27 -08001061}
reed70ee31b2015-12-10 13:44:45 -08001062
Mike Kleine083f7c2018-02-07 12:54:27 -05001063static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001064 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001065 if (prev.bytesPerPixel() <= 4 &&
1066 prev.colorType() != kRGBA_8888_SkColorType &&
1067 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001068 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1069 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1070 ct = kN32_SkColorType;
1071 }
1072 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001073}
1074
reed4960eee2015-12-18 07:09:18 -08001075void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001076 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001077 const SkRect* bounds = rec.fBounds;
1078 const SkPaint* paint = rec.fPaint;
1079 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1080
Mike Reed5532c2a2019-02-23 12:00:32 -05001081 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1082 // regardless of any hint-rect from the caller. skbug.com/8783
1083 if (rec.fBackdrop) {
1084 bounds = nullptr;
1085 }
1086
reed8c30a812016-04-20 16:36:51 -07001087 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001088 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001089 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001090 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001091
reed8c30a812016-04-20 16:36:51 -07001092 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001093 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1094 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1095 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001096 *
1097 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001098 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1099 * if necessary.
1100 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1101 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001102 * 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 -04001103 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001104 * of the original imagefilter, and draw that (via drawSprite)
1105 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1106 *
1107 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1108 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1109 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001110 if (imageFilter) {
1111 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001112 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1113 &modifiedCTM);
1114 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001115 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001116 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001117 modifiedRec = fMCRec;
1118 this->internalSetMatrix(modifiedCTM);
1119 SkPaint* p = lazyP.set(*paint);
1120 p->setImageFilter(std::move(modifiedFilter));
1121 imageFilter = p->getImageFilter();
1122 paint = p;
1123 }
1124 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1125 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001126 }
reed8c30a812016-04-20 16:36:51 -07001127
junov@chromium.orga907ac32012-02-24 21:54:07 +00001128 // do this before we create the layer. We don't call the public save() since
1129 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001130 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001131
junov@chromium.orga907ac32012-02-24 21:54:07 +00001132 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001133 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001134 if (modifiedRec) {
1135 // In this case there will be no layer in which to stash the matrix so we need to
1136 // revert the prior MCRec to its earlier state.
1137 modifiedRec->fMatrix = stashedMatrix;
1138 }
reed2ff1fce2014-12-11 07:07:37 -08001139 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 }
1141
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001142 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1143 // the clipRectBounds() call above?
1144 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001145 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001146 }
1147
reed8dc0ccb2015-03-20 06:32:52 -07001148 SkPixelGeometry geo = fProps.pixelGeometry();
1149 if (paint) {
reed76033be2015-03-14 10:54:31 -07001150 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001151 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001152 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001153 }
1154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155
robertphillips5139e502016-07-19 05:10:40 -07001156 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001157 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001158 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001159 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001160 }
reedb2db8982014-11-13 12:41:02 -08001161
Mike Kleine083f7c2018-02-07 12:54:27 -05001162 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001163 if (rec.fSaveLayerFlags & kF16ColorType) {
1164 info = info.makeColorType(kRGBA_F16_SkColorType);
1165 }
reed129ed1c2016-02-22 06:42:31 -08001166
Hal Canary704cd322016-11-07 14:13:52 -05001167 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001168 {
Florin Malita4571e492019-07-16 10:25:58 -04001169 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001170 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001171 const bool trackCoverage =
1172 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001173 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001174 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001175 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001176 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1177 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001178 return;
reed61f501f2015-04-29 08:34:00 -07001179 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001180 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001181 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182
Mike Reedb43a3e02017-02-11 10:18:58 -05001183 // only have a "next" if this new layer doesn't affect the clip (rare)
1184 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 fMCRec->fLayer = layer;
1186 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001187
Mike Reedc61abee2017-02-28 17:45:27 -05001188 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001189 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001190 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001191 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001192
Mike Reedc42a1cd2017-02-14 14:25:14 -05001193 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1194
1195 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1196 if (layer->fNext) {
1197 // need to punch a hole in the previous device, so we don't draw there, given that
1198 // the new top-layer will allow drawing to happen "below" it.
1199 SkRegion hole(ir);
1200 do {
1201 layer = layer->fNext;
1202 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1203 } while (layer->fNext);
1204 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205}
1206
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001207int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001208 if (0xFF == alpha) {
1209 return this->saveLayer(bounds, nullptr);
1210 } else {
1211 SkPaint tmpPaint;
1212 tmpPaint.setAlpha(alpha);
1213 return this->saveLayer(bounds, &tmpPaint);
1214 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001215}
1216
Mike Reed148b7fd2018-12-18 17:38:18 -05001217void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001218 SkIRect devBounds;
1219 if (localBounds) {
1220 SkRect tmp;
1221 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1222 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1223 devBounds.setEmpty();
1224 }
1225 } else {
1226 devBounds = this->getDeviceClipBounds();
1227 }
1228 if (devBounds.isEmpty()) {
1229 return;
1230 }
1231
Mike Reed148b7fd2018-12-18 17:38:18 -05001232 SkBaseDevice* device = this->getTopDevice();
1233 if (nullptr == device) { // Do we still need this check???
1234 return;
1235 }
1236
Michael Ludwig915b7792019-10-22 17:40:41 +00001237 // need the bounds relative to the device itself
1238 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
Mike Reed148b7fd2018-12-18 17:38:18 -05001239
Michael Ludwigac352122019-08-28 21:03:05 +00001240 // This is getting the special image from the current device, which is then drawn into (both by
1241 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1242 // own device, we need to explicitly copy the back image contents so that its original content
1243 // is available when we splat it back later during restore.
1244 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001245 if (!backImage) {
1246 return;
1247 }
1248
1249 // we really need the save, so we can wack the fMCRec
1250 this->checkForDeferredSave();
1251
1252 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1253
1254 SkPaint paint;
1255 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001256 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001257}
1258
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259void SkCanvas::internalRestore() {
1260 SkASSERT(fMCStack.count() != 0);
1261
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001262 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 DeviceCM* layer = fMCRec->fLayer; // may be null
1264 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001265 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266
Mike Reed148b7fd2018-12-18 17:38:18 -05001267 // move this out before we do the actual restore
1268 auto backImage = std::move(fMCRec->fBackImage);
1269
Mike Reedb18e74d2020-01-16 13:58:22 -05001270 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1271 fCameraStack.pop_back();
1272 }
1273
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 // now do the normal restore()
1275 fMCRec->~MCRec(); // balanced in save()
1276 fMCStack.pop_back();
1277 fMCRec = (MCRec*)fMCStack.back();
1278
Mike Reedc42a1cd2017-02-14 14:25:14 -05001279 if (fMCRec) {
1280 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1281 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001282
Mike Reed148b7fd2018-12-18 17:38:18 -05001283 if (backImage) {
1284 SkPaint paint;
1285 paint.setBlendMode(SkBlendMode::kDstOver);
1286 const int x = backImage->fLoc.x();
1287 const int y = backImage->fLoc.y();
1288 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1289 nullptr, SkMatrix::I());
1290 }
1291
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1293 since if we're being recorded, we don't want to record this (the
1294 recorder will have already recorded the restore).
1295 */
bsalomon49f085d2014-09-05 13:34:00 -07001296 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001297 if (fMCRec) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001298 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001299 layer->fDevice->setImmutable();
Michael Ludwig915b7792019-10-22 17:40:41 +00001300 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
1301 layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001302 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001303 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001304 this->internalSetMatrix(layer->fStashedMatrix);
Michael Ludwig915b7792019-10-22 17:40:41 +00001305 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001306 delete layer;
reedb679ca82015-04-07 04:40:48 -07001307 } else {
1308 // we're at the root
reeda499f902015-05-01 09:34:31 -07001309 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001310 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001311 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001313 }
msarettfbfa2582016-08-12 08:29:08 -07001314
1315 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001316 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001317 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319}
1320
reede8f30622016-03-23 18:59:25 -07001321sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001322 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001323 props = &fProps;
1324 }
1325 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001326}
1327
reede8f30622016-03-23 18:59:25 -07001328sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001329 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001330 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001331}
1332
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001333SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001334 return this->onImageInfo();
1335}
1336
1337SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001338 SkBaseDevice* dev = this->getDevice();
1339 if (dev) {
1340 return dev->imageInfo();
1341 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001342 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001343 }
1344}
1345
brianosman898235c2016-04-06 07:38:23 -07001346bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001347 return this->onGetProps(props);
1348}
1349
1350bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001351 SkBaseDevice* dev = this->getDevice();
1352 if (dev) {
1353 if (props) {
1354 *props = fProps;
1355 }
1356 return true;
1357 } else {
1358 return false;
1359 }
1360}
1361
reed6ceeebd2016-03-09 14:26:26 -08001362bool SkCanvas::peekPixels(SkPixmap* pmap) {
1363 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001364}
1365
reed884e97c2015-05-26 11:31:54 -07001366bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001367 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001368 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001369}
1370
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001371void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001372 SkPixmap pmap;
1373 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001374 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001375 }
1376 if (info) {
1377 *info = pmap.info();
1378 }
1379 if (rowBytes) {
1380 *rowBytes = pmap.rowBytes();
1381 }
1382 if (origin) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001383 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001384 }
reed884e97c2015-05-26 11:31:54 -07001385 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001386}
1387
reed884e97c2015-05-26 11:31:54 -07001388bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001389 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001390 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001391}
1392
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394
Mike Reed8bcd1282019-03-13 16:51:54 -04001395// In our current design/features, we should never have a layer (src) in a different colorspace
1396// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1397// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1398// colorspace.
1399static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1400 SkASSERT(src == dst);
1401}
1402
Michael Ludwig915b7792019-10-22 17:40:41 +00001403void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001404 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001406 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 paint = &tmp;
1408 }
reed@google.com4b226022011-01-11 18:32:13 +00001409
Mike Reed38992392019-07-30 10:48:15 -04001410 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001413 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001414 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1415 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001416 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001417 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwig915b7792019-10-22 17:40:41 +00001418 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001419 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001420 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1421 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001422 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1423 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001424 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1425 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001426 }
reed@google.com76dd2772012-01-05 21:15:07 +00001427 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001428 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001429 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 }
reeda2217ef2016-07-20 06:04:34 -07001431
Mike Reed38992392019-07-30 10:48:15 -04001432 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433}
1434
reed32704672015-12-16 08:27:10 -08001435/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001436
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001437void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001438 if (dx || dy) {
1439 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001440 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001441
reedfe69b502016-09-12 06:31:48 -07001442 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001443 // However, if either is non-finite, we might still complicate the matrix type,
1444 // so we still have to compute this.
1445 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001446
Mike Reedc42a1cd2017-02-14 14:25:14 -05001447 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001448
reedfe69b502016-09-12 06:31:48 -07001449 this->didTranslate(dx,dy);
1450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001453void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001454#ifdef SK_SUPPORT_LEGACY_CANVAS_MATRIX_VIRTUALS
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001455 SkMatrix m;
1456 m.setScale(sx, sy);
1457 this->concat(m);
Mike Reed9403c382020-01-13 14:40:56 +00001458#else
1459 if (sx != 1 || sy != 1) {
1460 this->checkForDeferredSave();
1461 fMCRec->fMatrix.preScale(sx, sy);
1462
1463 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1464 // but pre-scaling by a non-finite does change it, so we have to recompute.
1465 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1466
1467 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1468
1469 this->didScale(sx, sy);
1470 }
1471#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001475 SkMatrix m;
1476 m.setRotate(degrees);
1477 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
bungeman7438bfc2016-07-12 15:01:19 -07001480void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1481 SkMatrix m;
1482 m.setRotate(degrees, px, py);
1483 this->concat(m);
1484}
1485
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001486void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001487 SkMatrix m;
1488 m.setSkew(sx, sy);
1489 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001490}
1491
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001492void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001493 if (matrix.isIdentity()) {
1494 return;
1495 }
1496
reed2ff1fce2014-12-11 07:07:37 -08001497 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001498 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001499
msarett9637ea92016-08-18 14:03:30 -07001500 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001501
Mike Reed7627fa52017-02-08 10:07:53 -05001502 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001503
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001504 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505}
1506
Mike Reedc43f2a02020-01-16 14:54:34 -05001507void SkCanvas::experimental_concat44(const SkScalar m[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001508 this->checkForDeferredSave();
1509
Mike Reedb18e74d2020-01-16 13:58:22 -05001510 fMCRec->fMatrix.preConcat16(m);
Mike Reed403c8072020-01-08 10:40:39 -05001511
1512 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1513
1514 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1515
1516 this->didConcat44(m);
1517}
Mike Reed064c7f92020-01-08 17:33:04 -05001518
Mike Reedd4d3b332020-01-16 16:34:34 -05001519void SkCanvas::experimental_concat44(const SkMatrix44& m) {
Mike Reedc43f2a02020-01-16 14:54:34 -05001520 this->experimental_concat44(m.values());
Mike Reed064c7f92020-01-08 17:33:04 -05001521}
Mike Reed403c8072020-01-08 10:40:39 -05001522
Mike Reedee3216d2020-01-17 17:35:04 -05001523void SkCanvas::experimental_concat44(const SkM44& m) {
1524 this->experimental_concat44(m.asColMajor());
1525}
1526
reed8c30a812016-04-20 16:36:51 -07001527void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001528 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001529 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001530
Mike Reedc42a1cd2017-02-14 14:25:14 -05001531 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001532}
1533
1534void SkCanvas::setMatrix(const SkMatrix& matrix) {
1535 this->checkForDeferredSave();
1536 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001537 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538}
1539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001541 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542}
1543
1544//////////////////////////////////////////////////////////////////////////////
1545
Mike Reedc1f77742016-12-09 09:00:50 -05001546void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001547 if (!rect.isFinite()) {
1548 return;
1549 }
reed2ff1fce2014-12-11 07:07:37 -08001550 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1552 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001553}
1554
Mike Reedc1f77742016-12-09 09:00:50 -05001555void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001556 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001557
Mike Reed7627fa52017-02-08 10:07:53 -05001558 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001559
reedc64eff52015-11-21 12:39:45 -08001560 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001561 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1562 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001563 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564}
1565
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001566void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1567 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001568 if (fClipRestrictionRect.isEmpty()) {
1569 // we notify the device, but we *dont* resolve deferred saves (since we're just
1570 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001571 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001572 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001573 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001574 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001575 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001576 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001577 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1578 }
1579}
1580
Mike Reedc1f77742016-12-09 09:00:50 -05001581void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001582 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001584 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1586 } else {
1587 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001588 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001589}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001590
Mike Reedc1f77742016-12-09 09:00:50 -05001591void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001592 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001593
Brian Salomona3b45d42016-10-03 11:36:16 -04001594 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001595
Mike Reed7627fa52017-02-08 10:07:53 -05001596 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001597
Mike Reed20800c82017-11-15 16:09:04 -05001598 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1599 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001600 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001601}
1602
Mike Reedc1f77742016-12-09 09:00:50 -05001603void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001604 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001605 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001606
1607 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1608 SkRect r;
1609 if (path.isRect(&r)) {
1610 this->onClipRect(r, op, edgeStyle);
1611 return;
1612 }
1613 SkRRect rrect;
1614 if (path.isOval(&r)) {
1615 rrect.setOval(r);
1616 this->onClipRRect(rrect, op, edgeStyle);
1617 return;
1618 }
1619 if (path.isRRect(&rrect)) {
1620 this->onClipRRect(rrect, op, edgeStyle);
1621 return;
1622 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623 }
robertphillips39f05382015-11-24 09:30:12 -08001624
1625 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626}
1627
Mike Reedc1f77742016-12-09 09:00:50 -05001628void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001629 AutoValidateClip avc(this);
1630
Brian Salomona3b45d42016-10-03 11:36:16 -04001631 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001632
Mike Reed7627fa52017-02-08 10:07:53 -05001633 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634
Brian Salomona3b45d42016-10-03 11:36:16 -04001635 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001636 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001637 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001638 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639}
1640
Mike Reedc1f77742016-12-09 09:00:50 -05001641void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001642 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001643 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001644}
1645
Mike Reedc1f77742016-12-09 09:00:50 -05001646void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001647 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001648
reed@google.com5c3d1472011-02-22 19:12:23 +00001649 AutoValidateClip avc(this);
1650
Mike Reed20800c82017-11-15 16:09:04 -05001651 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001652 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653}
1654
reed@google.com819c9212011-02-23 18:56:55 +00001655#ifdef SK_DEBUG
1656void SkCanvas::validateClip() const {
1657 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001658 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001659 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001660 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001661 return;
1662 }
reed@google.com819c9212011-02-23 18:56:55 +00001663}
1664#endif
1665
Mike Reeda1361362017-03-07 09:37:29 -05001666bool SkCanvas::androidFramework_isClipAA() const {
1667 bool containsAA = false;
1668
1669 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1670
1671 return containsAA;
1672}
1673
1674class RgnAccumulator {
1675 SkRegion* fRgn;
1676public:
1677 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1678 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1679 SkIPoint origin = device->getOrigin();
1680 if (origin.x() | origin.y()) {
1681 rgn->translate(origin.x(), origin.y());
1682 }
1683 fRgn->op(*rgn, SkRegion::kUnion_Op);
1684 }
1685};
1686
1687void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1688 RgnAccumulator accum(rgn);
1689 SkRegion tmp;
1690
1691 rgn->setEmpty();
1692 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001693}
1694
reed@google.com5c3d1472011-02-22 19:12:23 +00001695///////////////////////////////////////////////////////////////////////////////
1696
reed@google.com754de5f2014-02-24 19:38:20 +00001697bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001698 return fMCRec->fRasterClip.isEmpty();
1699
1700 // TODO: should we only use the conservative answer in a recording canvas?
1701#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001702 SkBaseDevice* dev = this->getTopDevice();
1703 // if no device we return true
1704 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001705#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001706}
1707
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001708bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001709 SkBaseDevice* dev = this->getTopDevice();
1710 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001711 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001712}
1713
msarettfbfa2582016-08-12 08:29:08 -07001714static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1715#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1716 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1717 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1718 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1719 return 0xF != _mm_movemask_ps(mask);
1720#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1721 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1722 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1723 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1724 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1725#else
1726 SkRect devRectAsRect;
1727 SkRect devClipAsRect;
1728 devRect.store(&devRectAsRect.fLeft);
1729 devClip.store(&devClipAsRect.fLeft);
1730 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1731#endif
1732}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001733
msarettfbfa2582016-08-12 08:29:08 -07001734// It's important for this function to not be inlined. Otherwise the compiler will share code
1735// between the fast path and the slow path, resulting in two slow paths.
1736static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1737 const SkMatrix& matrix) {
1738 SkRect deviceRect;
1739 matrix.mapRect(&deviceRect, src);
1740 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1741}
1742
1743bool SkCanvas::quickReject(const SkRect& src) const {
1744#ifdef SK_DEBUG
1745 // Verify that fDeviceClipBounds are set properly.
1746 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001747 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001748 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001749 } else {
msarettfbfa2582016-08-12 08:29:08 -07001750 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 }
msarettfbfa2582016-08-12 08:29:08 -07001752
msarett9637ea92016-08-18 14:03:30 -07001753 // Verify that fIsScaleTranslate is set properly.
1754 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001755#endif
1756
msarett9637ea92016-08-18 14:03:30 -07001757 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001758 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1759 }
1760
1761 // We inline the implementation of mapScaleTranslate() for the fast path.
1762 float sx = fMCRec->fMatrix.getScaleX();
1763 float sy = fMCRec->fMatrix.getScaleY();
1764 float tx = fMCRec->fMatrix.getTranslateX();
1765 float ty = fMCRec->fMatrix.getTranslateY();
1766 Sk4f scale(sx, sy, sx, sy);
1767 Sk4f trans(tx, ty, tx, ty);
1768
1769 // Apply matrix.
1770 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1771
1772 // Make sure left < right, top < bottom.
1773 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1774 Sk4f min = Sk4f::Min(ltrb, rblt);
1775 Sk4f max = Sk4f::Max(ltrb, rblt);
1776 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1777 // ARM this sequence generates the fastest (a single instruction).
1778 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1779
1780 // Check if the device rect is NaN or outside the clip.
1781 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782}
1783
reed@google.com3b3e8952012-08-16 20:53:31 +00001784bool SkCanvas::quickReject(const SkPath& path) const {
1785 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
Mike Klein83c8dd92017-11-28 17:08:45 -05001788SkRect SkCanvas::getLocalClipBounds() const {
1789 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001790 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001791 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 }
1793
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001794 SkMatrix inverse;
1795 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001796 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001797 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001798 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799
Mike Reed42e8c532017-01-23 14:09:13 -05001800 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001801 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001802 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001803
Mike Reedb57b9312018-04-23 12:12:54 -04001804 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001805 inverse.mapRect(&bounds, r);
1806 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807}
1808
Mike Klein83c8dd92017-11-28 17:08:45 -05001809SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001810 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001811}
1812
Mike Reedb18e74d2020-01-16 13:58:22 -05001813///////////////////////////////////////////////////////////////////////
1814
1815SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1816 : fMCRec(owner)
1817 , fCamera(camera)
1818{
1819 // assumes the mcrec has already been concatenated with the camera
1820 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1821 fInvPostCamera.setIdentity();
1822 }
1823}
1824
Mike Reed403c8072020-01-08 10:40:39 -05001825SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001826 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827}
1828
Mike Reedc43f2a02020-01-16 14:54:34 -05001829SkM44 SkCanvas::experimental_getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001830 return fMCRec->fMatrix;
1831}
1832
Mike Reedc43f2a02020-01-16 14:54:34 -05001833SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001834 if (fCameraStack.empty()) {
Mike Reedc43f2a02020-01-16 14:54:34 -05001835 return this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001836 } else {
1837 const auto& top = fCameraStack.back();
Mike Reedc43f2a02020-01-16 14:54:34 -05001838 return top.fInvPostCamera * this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001839 }
1840}
1841
Mike Reedc43f2a02020-01-16 14:54:34 -05001842SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001843 if (fCameraStack.empty()) {
Mike Reedc43f2a02020-01-16 14:54:34 -05001844 return this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001845 } else {
1846 const auto& top = fCameraStack.back();
Mike Reedc43f2a02020-01-16 14:54:34 -05001847 return top.fCamera * top.fInvPostCamera * this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001848 }
1849}
1850
Brian Osman11052242016-10-27 14:47:55 -04001851GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001852 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001853 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001854}
1855
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001856GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001857 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001858 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001859}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001860
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001861void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1862 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001863 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001864 if (outer.isEmpty()) {
1865 return;
1866 }
1867 if (inner.isEmpty()) {
1868 this->drawRRect(outer, paint);
1869 return;
1870 }
1871
1872 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001873 // be able to return ...
1874 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001875 //
1876 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001877 if (!outer.getBounds().contains(inner.getBounds())) {
1878 return;
1879 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001880
1881 this->onDrawDRRect(outer, inner, paint);
1882}
1883
reed41af9662015-01-05 07:49:08 -08001884void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001885 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001886 this->onDrawPaint(paint);
1887}
1888
1889void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001890 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001891 // To avoid redundant logic in our culling code and various backends, we always sort rects
1892 // before passing them along.
1893 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001894}
1895
Mike Reedd5674082019-04-19 15:00:47 -04001896void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1897 TRACE_EVENT0("skia", TRACE_FUNC);
1898 this->onDrawBehind(paint);
1899}
1900
msarettdca352e2016-08-26 06:37:45 -07001901void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001902 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001903 if (region.isEmpty()) {
1904 return;
1905 }
1906
1907 if (region.isRect()) {
1908 return this->drawIRect(region.getBounds(), paint);
1909 }
1910
1911 this->onDrawRegion(region, paint);
1912}
1913
reed41af9662015-01-05 07:49:08 -08001914void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001915 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001916 // To avoid redundant logic in our culling code and various backends, we always sort rects
1917 // before passing them along.
1918 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001919}
1920
1921void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001922 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001923 this->onDrawRRect(rrect, paint);
1924}
1925
1926void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001927 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001928 this->onDrawPoints(mode, count, pts, paint);
1929}
1930
Mike Reede88a1cb2017-03-17 09:50:46 -04001931void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1932 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001933 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001934 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001935 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1936 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001937 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001938}
1939
1940void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001941 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001942 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001943 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1944}
1945
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001946void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1947 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001948 TRACE_EVENT0("skia", TRACE_FUNC);
1949 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001950 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001951 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1952}
1953
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001954void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1955 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001956 TRACE_EVENT0("skia", TRACE_FUNC);
1957 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001958 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001959 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001960}
1961
1962void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001963 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001964 this->onDrawPath(path, paint);
1965}
1966
reeda85d4d02015-05-06 12:56:48 -07001967void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001968 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001969 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001970 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001971}
1972
Mike Reedc4e31092018-01-30 11:15:27 -05001973// Returns true if the rect can be "filled" : non-empty and finite
1974static bool fillable(const SkRect& r) {
1975 SkScalar w = r.width();
1976 SkScalar h = r.height();
1977 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1978}
1979
reede47829b2015-08-06 10:02:53 -07001980void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1981 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001982 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001983 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001984 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001985 return;
1986 }
1987 this->onDrawImageRect(image, &src, dst, paint, constraint);
1988}
reed41af9662015-01-05 07:49:08 -08001989
reed84984ef2015-07-17 07:09:43 -07001990void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1991 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001992 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001993 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001994}
1995
Brian Salomonf08002c2018-10-26 16:15:46 -04001996void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001997 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001998 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001999 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07002000}
reede47829b2015-08-06 10:02:53 -07002001
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002002namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04002003class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002004public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002005 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2006 if (!origPaint) {
2007 return;
2008 }
2009 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2010 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2011 }
2012 if (origPaint->getMaskFilter()) {
2013 fPaint.writable()->setMaskFilter(nullptr);
2014 }
2015 if (origPaint->isAntiAlias()) {
2016 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002017 }
2018 }
2019
2020 const SkPaint* get() const {
2021 return fPaint;
2022 }
2023
2024private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002025 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002026};
2027} // namespace
2028
reed4c21dc52015-06-25 12:32:03 -07002029void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2030 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002031 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002032 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002033 if (dst.isEmpty()) {
2034 return;
2035 }
msarett552bca92016-08-03 06:53:26 -07002036 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002037 LatticePaint latticePaint(paint);
2038 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002039 } else {
reede47829b2015-08-06 10:02:53 -07002040 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002041 }
reed4c21dc52015-06-25 12:32:03 -07002042}
2043
msarett16882062016-08-16 09:31:08 -07002044void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2045 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002046 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002047 RETURN_ON_NULL(image);
2048 if (dst.isEmpty()) {
2049 return;
2050 }
msarett71df2d72016-09-30 12:41:42 -07002051
2052 SkIRect bounds;
2053 Lattice latticePlusBounds = lattice;
2054 if (!latticePlusBounds.fBounds) {
2055 bounds = SkIRect::MakeWH(image->width(), image->height());
2056 latticePlusBounds.fBounds = &bounds;
2057 }
2058
2059 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002060 LatticePaint latticePaint(paint);
2061 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002062 } else {
2063 this->drawImageRect(image, dst, paint);
2064 }
2065}
2066
reed41af9662015-01-05 07:49:08 -08002067void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002068 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002069 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002070 return;
2071 }
reed41af9662015-01-05 07:49:08 -08002072 this->onDrawBitmap(bitmap, dx, dy, paint);
2073}
2074
reede47829b2015-08-06 10:02:53 -07002075void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002076 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002077 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002078 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002079 return;
2080 }
reede47829b2015-08-06 10:02:53 -07002081 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002082}
2083
reed84984ef2015-07-17 07:09:43 -07002084void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2085 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002086 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002087}
2088
reede47829b2015-08-06 10:02:53 -07002089void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2090 SrcRectConstraint constraint) {
2091 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2092 constraint);
2093}
reede47829b2015-08-06 10:02:53 -07002094
reed41af9662015-01-05 07:49:08 -08002095void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2096 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002097 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002098 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002099 return;
2100 }
msarett552bca92016-08-03 06:53:26 -07002101 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002102 LatticePaint latticePaint(paint);
2103 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002104 } else {
reeda5517e22015-07-14 10:54:12 -07002105 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002106 }
reed41af9662015-01-05 07:49:08 -08002107}
2108
msarettc573a402016-08-02 08:05:56 -07002109void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2110 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002111 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002112 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002113 return;
2114 }
msarett71df2d72016-09-30 12:41:42 -07002115
2116 SkIRect bounds;
2117 Lattice latticePlusBounds = lattice;
2118 if (!latticePlusBounds.fBounds) {
2119 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2120 latticePlusBounds.fBounds = &bounds;
2121 }
2122
2123 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002124 LatticePaint latticePaint(paint);
2125 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002126 } else {
msarett16882062016-08-16 09:31:08 -07002127 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002128 }
msarettc573a402016-08-02 08:05:56 -07002129}
2130
reed71c3c762015-06-24 10:29:17 -07002131void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002132 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002133 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002134 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002135 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002136 if (count <= 0) {
2137 return;
2138 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002139 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002140 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002141 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002142}
2143
reedf70b5312016-03-04 16:36:20 -08002144void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002145 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002146 if (key) {
2147 this->onDrawAnnotation(rect, key, value);
2148 }
2149}
2150
reede47829b2015-08-06 10:02:53 -07002151void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2152 const SkPaint* paint, SrcRectConstraint constraint) {
2153 if (src) {
2154 this->drawImageRect(image, *src, dst, paint, constraint);
2155 } else {
2156 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2157 dst, paint, constraint);
2158 }
2159}
2160void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2161 const SkPaint* paint, SrcRectConstraint constraint) {
2162 if (src) {
2163 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2164 } else {
2165 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2166 dst, paint, constraint);
2167 }
2168}
2169
Mike Reed4204da22017-05-17 08:53:36 -04002170void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002171 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002172 this->onDrawShadowRec(path, rec);
2173}
2174
2175void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2176 SkPaint paint;
2177 const SkRect& pathBounds = path.getBounds();
2178
Mike Reed38992392019-07-30 10:48:15 -04002179 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002180 while (iter.next()) {
2181 iter.fDevice->drawShadow(path, rec);
2182 }
Mike Reed38992392019-07-30 10:48:15 -04002183 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002184}
2185
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002186void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002187 QuadAAFlags aaFlags, const SkColor4f& color,
2188 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002189 TRACE_EVENT0("skia", TRACE_FUNC);
2190 // Make sure the rect is sorted before passing it along
2191 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2192}
2193
2194void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2195 const SkPoint dstClips[],
2196 const SkMatrix preViewMatrices[],
2197 const SkPaint* paint,
2198 SrcRectConstraint constraint) {
2199 TRACE_EVENT0("skia", TRACE_FUNC);
2200 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2201}
2202
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203//////////////////////////////////////////////////////////////////////////////
2204// These are the virtual drawing methods
2205//////////////////////////////////////////////////////////////////////////////
2206
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002207void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002208 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002209 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2210 }
2211}
2212
reed41af9662015-01-05 07:49:08 -08002213void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002214 this->internalDrawPaint(paint);
2215}
2216
2217void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002218 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219
2220 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002221 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 }
2223
Mike Reed38992392019-07-30 10:48:15 -04002224 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225}
2226
reed41af9662015-01-05 07:49:08 -08002227void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2228 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002229 if ((long)count <= 0) {
2230 return;
2231 }
2232
Mike Reed822128b2017-02-28 16:41:03 -05002233 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002234 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002235 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002236 // special-case 2 points (common for drawing a single line)
2237 if (2 == count) {
2238 r.set(pts[0], pts[1]);
2239 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002240 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002241 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002242 if (!r.isFinite()) {
2243 return;
2244 }
Mike Reed822128b2017-02-28 16:41:03 -05002245 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002246 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2247 return;
2248 }
2249 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002250 }
reed@google.coma584aed2012-05-16 14:06:02 +00002251
halcanary96fcdcc2015-08-27 07:41:13 -07002252 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253
Mike Reed38992392019-07-30 10:48:15 -04002254 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002255
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002257 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 }
reed@google.com4b226022011-01-11 18:32:13 +00002259
Mike Reed38992392019-07-30 10:48:15 -04002260 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261}
2262
reed4a167172016-08-18 17:15:25 -07002263static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002264 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002265}
2266
reed41af9662015-01-05 07:49:08 -08002267void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002268 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002270 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002271 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002272 return;
2273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274 }
reed@google.com4b226022011-01-11 18:32:13 +00002275
reed4a167172016-08-18 17:15:25 -07002276 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002277 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278
reed4a167172016-08-18 17:15:25 -07002279 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002280 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002281 }
2282
Mike Reed38992392019-07-30 10:48:15 -04002283 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002284 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002285 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002286 SkDrawIter iter(this);
2287 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002288 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002289 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002291}
2292
msarett44df6512016-08-25 13:54:30 -07002293void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002294 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002295 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002296 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002297 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2298 return;
2299 }
msarett44df6512016-08-25 13:54:30 -07002300 }
2301
Mike Reed38992392019-07-30 10:48:15 -04002302 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002303
2304 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002305 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002306 }
2307
Mike Reed38992392019-07-30 10:48:15 -04002308 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002309}
2310
Mike Reedd5674082019-04-19 15:00:47 -04002311void SkCanvas::onDrawBehind(const SkPaint& paint) {
2312 SkIRect bounds;
2313 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2314 for (;;) {
2315 const MCRec* rec = (const MCRec*)iter.prev();
2316 if (!rec) {
2317 return; // no backimages, so nothing to draw
2318 }
2319 if (rec->fBackImage) {
2320 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2321 rec->fBackImage->fImage->width(),
2322 rec->fBackImage->fImage->height());
2323 break;
2324 }
2325 }
2326
Mike Reed38992392019-07-30 10:48:15 -04002327 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002328
2329 while (iter.next()) {
2330 SkBaseDevice* dev = iter.fDevice;
2331
Mike Reedd5674082019-04-19 15:00:47 -04002332 dev->save();
2333 // We use clipRegion because it is already defined to operate in dev-space
2334 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2335 // but we don't want that, so we undo that before calling in.
Michael Ludwig915b7792019-10-22 17:40:41 +00002336 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002337 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002338 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002339 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002340 }
2341
Mike Reed38992392019-07-30 10:48:15 -04002342 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002343}
2344
reed41af9662015-01-05 07:49:08 -08002345void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002346 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002347 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002348 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002349 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002350 return;
2351 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002352 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002353
Mike Reed38992392019-07-30 10:48:15 -04002354 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002355
2356 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002357 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002358 }
2359
Mike Reed38992392019-07-30 10:48:15 -04002360 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002361}
2362
bsalomonac3aa242016-08-19 11:25:19 -07002363void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2364 SkScalar sweepAngle, bool useCenter,
2365 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002366 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002367 if (paint.canComputeFastBounds()) {
2368 SkRect storage;
2369 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002370 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002371 return;
2372 }
bsalomonac3aa242016-08-19 11:25:19 -07002373 }
2374
Mike Reed38992392019-07-30 10:48:15 -04002375 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002376
2377 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002378 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002379 }
2380
Mike Reed38992392019-07-30 10:48:15 -04002381 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002382}
2383
reed41af9662015-01-05 07:49:08 -08002384void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002385 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002386 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002387 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2388 return;
2389 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002390 }
2391
2392 if (rrect.isRect()) {
2393 // call the non-virtual version
2394 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002395 return;
2396 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002397 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002398 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2399 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002400 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002401
Mike Reed38992392019-07-30 10:48:15 -04002402 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002403
2404 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002405 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002406 }
2407
Mike Reed38992392019-07-30 10:48:15 -04002408 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002409}
2410
Mike Reed822128b2017-02-28 16:41:03 -05002411void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002412 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002413 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002414 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2415 return;
2416 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002417 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002418
Mike Reed38992392019-07-30 10:48:15 -04002419 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002420
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002421 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002422 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002423 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002424
Mike Reed38992392019-07-30 10:48:15 -04002425 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002426}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002427
reed41af9662015-01-05 07:49:08 -08002428void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002429 if (!path.isFinite()) {
2430 return;
2431 }
2432
Mike Reed822128b2017-02-28 16:41:03 -05002433 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002434 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002435 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002436 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2437 return;
2438 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002440
Mike Reed822128b2017-02-28 16:41:03 -05002441 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002442 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002443 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002444 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002445 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002446 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002447
Mike Reed38992392019-07-30 10:48:15 -04002448 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002449
2450 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002451 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002452 }
2453
Mike Reed38992392019-07-30 10:48:15 -04002454 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002455}
2456
reed262a71b2015-12-05 13:07:27 -08002457bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002458 if (!paint.getImageFilter()) {
2459 return false;
2460 }
2461
2462 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002463 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002464 return false;
2465 }
2466
2467 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2468 // Once we can filter and the filter will return a result larger than itself, we should be
2469 // able to remove this constraint.
2470 // skbug.com/4526
2471 //
2472 SkPoint pt;
2473 ctm.mapXY(x, y, &pt);
2474 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2475 return ir.contains(fMCRec->fRasterClip.getBounds());
2476}
2477
Mike Reedf441cfc2018-04-11 14:50:16 -04002478// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2479// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2480// null.
2481static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2482 if (paintParam) {
2483 *real = *paintParam;
2484 real->setStyle(SkPaint::kFill_Style);
2485 real->setPathEffect(nullptr);
2486 paintParam = real;
2487 }
2488 return paintParam;
2489}
2490
reeda85d4d02015-05-06 12:56:48 -07002491void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002492 SkPaint realPaint;
2493 paint = init_image_paint(&realPaint, paint);
2494
reeda85d4d02015-05-06 12:56:48 -07002495 SkRect bounds = SkRect::MakeXYWH(x, y,
2496 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002497 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002498 SkRect tmp = bounds;
2499 if (paint) {
2500 paint->computeFastBounds(tmp, &tmp);
2501 }
2502 if (this->quickReject(tmp)) {
2503 return;
2504 }
reeda85d4d02015-05-06 12:56:48 -07002505 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002506 // At this point we need a real paint object. If the caller passed null, then we should
2507 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2508 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2509 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002510
reeda2217ef2016-07-20 06:04:34 -07002511 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002512 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2513 *paint);
2514 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002515 special = this->getDevice()->makeSpecial(image);
2516 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002517 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002518 }
2519 }
2520
Mike Reed38992392019-07-30 10:48:15 -04002521 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002522
reeda85d4d02015-05-06 12:56:48 -07002523 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002524 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002525 if (special) {
2526 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002527 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002528 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002529 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002530 SkScalarRoundToInt(pt.fY), pnt,
2531 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002532 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002533 iter.fDevice->drawImageRect(
2534 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2535 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002536 }
reeda85d4d02015-05-06 12:56:48 -07002537 }
halcanary9d524f22016-03-29 09:03:52 -07002538
Mike Reed38992392019-07-30 10:48:15 -04002539 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002540}
2541
reed41af9662015-01-05 07:49:08 -08002542void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002543 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002544 SkPaint realPaint;
2545 paint = init_image_paint(&realPaint, paint);
2546
halcanary96fcdcc2015-08-27 07:41:13 -07002547 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002548 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002549 if (paint) {
2550 paint->computeFastBounds(dst, &storage);
2551 }
2552 if (this->quickReject(storage)) {
2553 return;
2554 }
reeda85d4d02015-05-06 12:56:48 -07002555 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002556 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002557
Mike Reed38992392019-07-30 10:48:15 -04002558 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002559
reeda85d4d02015-05-06 12:56:48 -07002560 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002561 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002562 }
halcanary9d524f22016-03-29 09:03:52 -07002563
Mike Reed38992392019-07-30 10:48:15 -04002564 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002565}
2566
reed41af9662015-01-05 07:49:08 -08002567void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568 SkDEBUGCODE(bitmap.validate();)
2569
reed33366972015-10-08 09:22:02 -07002570 if (bitmap.drawsNothing()) {
2571 return;
2572 }
2573
Mike Reedf441cfc2018-04-11 14:50:16 -04002574 SkPaint realPaint;
2575 init_image_paint(&realPaint, paint);
2576 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002577
Mike Reed822128b2017-02-28 16:41:03 -05002578 SkRect bounds;
2579 bitmap.getBounds(&bounds);
2580 bounds.offset(x, y);
2581 bool canFastBounds = paint->canComputeFastBounds();
2582 if (canFastBounds) {
2583 SkRect storage;
2584 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002585 return;
2586 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002587 }
reed@google.com4b226022011-01-11 18:32:13 +00002588
reeda2217ef2016-07-20 06:04:34 -07002589 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002590 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2591 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002592 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002593 special = this->getDevice()->makeSpecial(bitmap);
2594 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002595 drawAsSprite = false;
2596 }
2597 }
2598
Mike Reed38992392019-07-30 10:48:15 -04002599 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002600
2601 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002602 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002603 if (special) {
reed262a71b2015-12-05 13:07:27 -08002604 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002605 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002606 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002607 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002608 SkScalarRoundToInt(pt.fY), pnt,
2609 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002610 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002611 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2612 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2613 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002614 }
reed33366972015-10-08 09:22:02 -07002615 }
msarettfbfa2582016-08-12 08:29:08 -07002616
Mike Reed38992392019-07-30 10:48:15 -04002617 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002618}
2619
reed@google.com9987ec32011-09-07 11:57:52 +00002620// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002621void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002622 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002623 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002624 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002625 return;
2626 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002627
halcanary96fcdcc2015-08-27 07:41:13 -07002628 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002629 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002630 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2631 return;
2632 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002633 }
reed@google.com3d608122011-11-21 15:16:16 +00002634
reed@google.com33535f32012-09-25 15:37:50 +00002635 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002636 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002637 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002638 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002639
Mike Reed38992392019-07-30 10:48:15 -04002640 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002641
reed@google.com33535f32012-09-25 15:37:50 +00002642 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002643 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002644 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002645
Mike Reed38992392019-07-30 10:48:15 -04002646 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002647}
2648
reed41af9662015-01-05 07:49:08 -08002649void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002650 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002651 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002652 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002653}
2654
reed4c21dc52015-06-25 12:32:03 -07002655void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2656 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002657 SkPaint realPaint;
2658 paint = init_image_paint(&realPaint, paint);
2659
halcanary96fcdcc2015-08-27 07:41:13 -07002660 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002661 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002662 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2663 return;
2664 }
reed@google.com3d608122011-11-21 15:16:16 +00002665 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002666 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002667
Mike Reed38992392019-07-30 10:48:15 -04002668 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002669
reed4c21dc52015-06-25 12:32:03 -07002670 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002671 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002672 }
halcanary9d524f22016-03-29 09:03:52 -07002673
Mike Reed38992392019-07-30 10:48:15 -04002674 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002675}
2676
reed41af9662015-01-05 07:49:08 -08002677void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2678 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002679 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002680 SkPaint realPaint;
2681 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002682
halcanary96fcdcc2015-08-27 07:41:13 -07002683 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002684 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002685 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2686 return;
2687 }
reed4c21dc52015-06-25 12:32:03 -07002688 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002689 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002690
Mike Reed38992392019-07-30 10:48:15 -04002691 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002692
reed4c21dc52015-06-25 12:32:03 -07002693 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002694 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002695 }
halcanary9d524f22016-03-29 09:03:52 -07002696
Mike Reed38992392019-07-30 10:48:15 -04002697 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002698}
2699
msarett16882062016-08-16 09:31:08 -07002700void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2701 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002702 SkPaint realPaint;
2703 paint = init_image_paint(&realPaint, paint);
2704
msarett16882062016-08-16 09:31:08 -07002705 if (nullptr == paint || paint->canComputeFastBounds()) {
2706 SkRect storage;
2707 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2708 return;
2709 }
2710 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002711 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002712
Mike Reed38992392019-07-30 10:48:15 -04002713 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002714
2715 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002716 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002717 }
2718
Mike Reed38992392019-07-30 10:48:15 -04002719 DRAW_END
msarett16882062016-08-16 09:31:08 -07002720}
2721
2722void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2723 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002724 SkPaint realPaint;
2725 paint = init_image_paint(&realPaint, paint);
2726
msarett16882062016-08-16 09:31:08 -07002727 if (nullptr == paint || paint->canComputeFastBounds()) {
2728 SkRect storage;
2729 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2730 return;
2731 }
2732 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002733 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002734
Mike Reed38992392019-07-30 10:48:15 -04002735 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002736
2737 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002738 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002739 }
2740
Mike Reed38992392019-07-30 10:48:15 -04002741 DRAW_END
msarett16882062016-08-16 09:31:08 -07002742}
2743
fmalita00d5c2c2014-08-21 08:53:26 -07002744void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2745 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002746 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002747 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002748 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002749 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002750 SkRect tmp;
2751 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2752 return;
2753 }
2754 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002755 }
2756
fmalita024f9962015-03-03 19:08:17 -08002757 // We cannot filter in the looper as we normally do, because the paint is
2758 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002759 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002760
fmalitaaa1b9122014-08-28 14:32:24 -07002761 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002762 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002763 }
2764
Mike Reed38992392019-07-30 10:48:15 -04002765 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002766}
2767
Mike Reed358fcad2018-11-23 15:27:51 -05002768// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002769void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002770 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2771 TRACE_EVENT0("skia", TRACE_FUNC);
2772 if (byteLength) {
2773 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002774 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002775 }
2776}
Mike Reed4f81bb72019-01-23 09:23:00 -05002777
fmalita00d5c2c2014-08-21 08:53:26 -07002778void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2779 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002780 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002781 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002782 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002783 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002784}
reed@google.come0d9ce82014-04-23 04:00:17 +00002785
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002786void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002787 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002788 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002789
2790 while (iter.next()) {
2791 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002792 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002793 }
2794
Mike Reed38992392019-07-30 10:48:15 -04002795 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002796}
2797
dandovb3c9d1c2014-08-12 08:34:29 -07002798void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002799 const SkPoint texCoords[4], SkBlendMode bmode,
2800 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002801 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002802 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002803 return;
2804 }
mtklein6cfa73a2014-08-13 13:33:49 -07002805
Mike Reedfaba3712016-11-03 14:45:31 -04002806 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002807}
2808
2809void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002810 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002811 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002812 // Since a patch is always within the convex hull of the control points, we discard it when its
2813 // bounding rectangle is completely outside the current clip.
2814 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002815 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002816 if (this->quickReject(bounds)) {
2817 return;
2818 }
mtklein6cfa73a2014-08-13 13:33:49 -07002819
Mike Reed38992392019-07-30 10:48:15 -04002820 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002821
dandovecfff212014-08-04 10:02:00 -07002822 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002823 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002824 }
mtklein6cfa73a2014-08-13 13:33:49 -07002825
Mike Reed38992392019-07-30 10:48:15 -04002826 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002827}
2828
reeda8db7282015-07-07 10:22:31 -07002829void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002830#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002831 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002832#endif
reede3b38ce2016-01-08 09:18:44 -08002833 RETURN_ON_NULL(dr);
2834 if (x || y) {
2835 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2836 this->onDrawDrawable(dr, &matrix);
2837 } else {
2838 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002839 }
2840}
2841
reeda8db7282015-07-07 10:22:31 -07002842void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002843#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002844 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002845#endif
reede3b38ce2016-01-08 09:18:44 -08002846 RETURN_ON_NULL(dr);
2847 if (matrix && matrix->isIdentity()) {
2848 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002849 }
reede3b38ce2016-01-08 09:18:44 -08002850 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002851}
2852
2853void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002854 // drawable bounds are no longer reliable (e.g. android displaylist)
2855 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002856 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002857}
2858
reed71c3c762015-06-24 10:29:17 -07002859void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002860 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002861 const SkRect* cull, const SkPaint* paint) {
2862 if (cull && this->quickReject(*cull)) {
2863 return;
2864 }
2865
2866 SkPaint pnt;
2867 if (paint) {
2868 pnt = *paint;
2869 }
halcanary9d524f22016-03-29 09:03:52 -07002870
Mike Reed38992392019-07-30 10:48:15 -04002871 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002872 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002873 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002874 }
Mike Reed38992392019-07-30 10:48:15 -04002875 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002876}
2877
reedf70b5312016-03-04 16:36:20 -08002878void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2879 SkASSERT(key);
2880
2881 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002882 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002883 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002884 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002885 }
Mike Reed38992392019-07-30 10:48:15 -04002886 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002887}
2888
Michael Ludwiga595f862019-08-27 15:25:49 -04002889void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2890 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002891 SkASSERT(r.isSorted());
2892
2893 // If this used a paint, it would be a filled color with blend mode, which does not
2894 // need to use an autodraw loop, so use SkDrawIter directly.
2895 if (this->quickReject(r)) {
2896 return;
2897 }
2898
Michael Ludwiga4b44882019-08-28 14:34:58 -04002899 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002900 SkDrawIter iter(this);
2901 while(iter.next()) {
2902 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2903 }
2904}
2905
2906void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2907 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2908 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002909 if (count <= 0) {
2910 // Nothing to draw
2911 return;
2912 }
2913
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002914 SkPaint realPaint;
2915 init_image_paint(&realPaint, paint);
2916
Michael Ludwiga4b44882019-08-28 14:34:58 -04002917 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2918 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2919 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2920 // or we need it for the autolooper (since it greatly improves image filter perf).
2921 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2922 bool setBoundsValid = count == 1 || needsAutoLooper;
2923 SkRect setBounds = imageSet[0].fDstRect;
2924 if (imageSet[0].fMatrixIndex >= 0) {
2925 // Account for the per-entry transform that is applied prior to the CTM when drawing
2926 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002927 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002928 if (needsAutoLooper) {
2929 for (int i = 1; i < count; ++i) {
2930 SkRect entryBounds = imageSet[i].fDstRect;
2931 if (imageSet[i].fMatrixIndex >= 0) {
2932 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2933 }
2934 setBounds.joinPossiblyEmptyRect(entryBounds);
2935 }
2936 }
2937
2938 // If we happen to have the draw bounds, though, might as well check quickReject().
2939 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2940 SkRect tmp;
2941 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2942 return;
2943 }
2944 }
2945
2946 if (needsAutoLooper) {
2947 SkASSERT(setBoundsValid);
2948 DRAW_BEGIN(realPaint, &setBounds)
2949 while (iter.next()) {
2950 iter.fDevice->drawEdgeAAImageSet(
2951 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2952 }
2953 DRAW_END
2954 } else {
2955 this->predrawNotify();
2956 SkDrawIter iter(this);
2957 while(iter.next()) {
2958 iter.fDevice->drawEdgeAAImageSet(
2959 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2960 }
2961 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002962}
2963
reed@android.com8a1c16f2008-12-17 15:59:43 +00002964//////////////////////////////////////////////////////////////////////////////
2965// These methods are NOT virtual, and therefore must call back into virtual
2966// methods, rather than actually drawing themselves.
2967//////////////////////////////////////////////////////////////////////////////
2968
reed374772b2016-10-05 17:33:02 -07002969void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002970 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002971 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002972 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002973 this->drawPaint(paint);
2974}
2975
2976void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002977 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002978 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2979}
2980
Mike Reed3661bc92017-02-22 13:21:42 -05002981void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002982 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002983 pts[0].set(x0, y0);
2984 pts[1].set(x1, y1);
2985 this->drawPoints(kLines_PointMode, 2, pts, paint);
2986}
2987
Mike Reed3661bc92017-02-22 13:21:42 -05002988void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002989 if (radius < 0) {
2990 radius = 0;
2991 }
2992
2993 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002994 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002995 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002996}
2997
2998void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2999 const SkPaint& paint) {
3000 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003001 SkRRect rrect;
3002 rrect.setRectXY(r, rx, ry);
3003 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003004 } else {
3005 this->drawRect(r, paint);
3006 }
3007}
3008
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3010 SkScalar sweepAngle, bool useCenter,
3011 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04003012 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07003013 if (oval.isEmpty() || !sweepAngle) {
3014 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003015 }
bsalomon21af9ca2016-08-25 12:29:23 -07003016 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017}
3018
reed@android.comf76bacf2009-05-13 14:00:33 +00003019///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003020#ifdef SK_DISABLE_SKPICTURE
3021void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07003022
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003023
3024void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3025 const SkPaint* paint) {}
3026#else
Mike Klein88d90712018-01-27 17:30:04 +00003027/**
3028 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3029 * against the playback cost of recursing into the subpicture to get at its actual ops.
3030 *
3031 * For now we pick a conservatively small value, though measurement (and other heuristics like
3032 * the type of ops contained) may justify changing this value.
3033 */
3034#define kMaxPictureOpsToUnrollInsteadOfRef 1
3035
reedd5fa1a42014-08-09 11:08:05 -07003036void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04003037 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08003038 RETURN_ON_NULL(picture);
3039
reede3b38ce2016-01-08 09:18:44 -08003040 if (matrix && matrix->isIdentity()) {
3041 matrix = nullptr;
3042 }
Mike Klein88d90712018-01-27 17:30:04 +00003043 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3044 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3045 picture->playback(this);
3046 } else {
3047 this->onDrawPicture(picture, matrix, paint);
3048 }
reedd5fa1a42014-08-09 11:08:05 -07003049}
robertphillips9b14f262014-06-04 05:40:44 -07003050
reedd5fa1a42014-08-09 11:08:05 -07003051void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3052 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003053 if (!paint || paint->canComputeFastBounds()) {
3054 SkRect bounds = picture->cullRect();
3055 if (paint) {
3056 paint->computeFastBounds(bounds, &bounds);
3057 }
3058 if (matrix) {
3059 matrix->mapRect(&bounds);
3060 }
3061 if (this->quickReject(bounds)) {
3062 return;
3063 }
3064 }
3065
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003066 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003067 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003068}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003069#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003070
reed@android.com8a1c16f2008-12-17 15:59:43 +00003071///////////////////////////////////////////////////////////////////////////////
3072///////////////////////////////////////////////////////////////////////////////
3073
reed3aafe112016-08-18 12:45:34 -07003074SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003075 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003076
3077 SkASSERT(canvas);
3078
reed3aafe112016-08-18 12:45:34 -07003079 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003080 fDone = !fImpl->next();
3081}
3082
3083SkCanvas::LayerIter::~LayerIter() {
3084 fImpl->~SkDrawIter();
3085}
3086
3087void SkCanvas::LayerIter::next() {
3088 fDone = !fImpl->next();
3089}
3090
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003091SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003092 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003093}
3094
3095const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003096 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003097}
3098
3099const SkPaint& SkCanvas::LayerIter::paint() const {
3100 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003101 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003102 paint = &fDefaultPaint;
3103 }
3104 return *paint;
3105}
3106
Mike Reedca37f322018-03-08 13:22:16 -05003107SkIRect SkCanvas::LayerIter::clipBounds() const {
3108 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003109}
3110
Michael Ludwig915b7792019-10-22 17:40:41 +00003111int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3112int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003113
3114///////////////////////////////////////////////////////////////////////////////
3115
Brian Osmane8a98632019-04-10 10:26:10 -04003116SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3117SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3118SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3119SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3120
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003121SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3122 const SkRect& dstRect, int matrixIndex, float alpha,
3123 unsigned aaFlags, bool hasClip)
3124 : fImage(std::move(image))
3125 , fSrcRect(srcRect)
3126 , fDstRect(dstRect)
3127 , fMatrixIndex(matrixIndex)
3128 , fAlpha(alpha)
3129 , fAAFlags(aaFlags)
3130 , fHasClip(hasClip) {}
3131
3132SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3133 const SkRect& dstRect, float alpha, unsigned aaFlags)
3134 : fImage(std::move(image))
3135 , fSrcRect(srcRect)
3136 , fDstRect(dstRect)
3137 , fAlpha(alpha)
3138 , fAAFlags(aaFlags) {}
3139
3140///////////////////////////////////////////////////////////////////////////////
3141
Mike Reed5df49342016-11-12 08:06:55 -06003142std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003143 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003144 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003145 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003146 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003147
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003148 SkBitmap bitmap;
3149 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003150 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003151 }
Mike Reed12f77342017-11-08 11:19:52 -05003152
3153 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003154 std::make_unique<SkCanvas>(bitmap, *props) :
3155 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003156}
reedd5fa1a42014-08-09 11:08:05 -07003157
3158///////////////////////////////////////////////////////////////////////////////
3159
Florin Malitaee424ac2016-12-01 12:47:59 -05003160SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003161 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003162
Florin Malita439ace92016-12-02 12:05:41 -05003163SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003164 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003165
Herb Derbyefe39bc2018-05-01 17:06:20 -04003166SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003167 : INHERITED(device) {}
3168
Florin Malitaee424ac2016-12-01 12:47:59 -05003169SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3170 (void)this->INHERITED::getSaveLayerStrategy(rec);
3171 return kNoLayer_SaveLayerStrategy;
3172}
3173
Mike Reed148b7fd2018-12-18 17:38:18 -05003174bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3175 return false;
3176}
3177
Florin Malitaee424ac2016-12-01 12:47:59 -05003178///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003179
reed73603f32016-09-20 08:42:38 -07003180static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3181static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3182static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3183static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3184static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3185static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003186
3187///////////////////////////////////////////////////////////////////////////////////////////////////
3188
3189SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3190 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003191 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003192 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwig915b7792019-10-22 17:40:41 +00003193 SkIPoint origin = dev->getOrigin();
3194 SkMatrix ctm = this->getTotalMatrix();
3195 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3196
3197 SkIRect clip = fMCRec->fRasterClip.getBounds();
3198 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003199 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003200 clip.setEmpty();
3201 }
3202
Michael Ludwig915b7792019-10-22 17:40:41 +00003203 fAllocator->updateHandle(handle, ctm, clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003204 return handle;
3205 }
3206 return nullptr;
3207}
3208
3209static bool install(SkBitmap* bm, const SkImageInfo& info,
3210 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003211 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003212}
3213
3214SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3215 SkBitmap* bm) {
3216 SkRasterHandleAllocator::Rec rec;
3217 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3218 return nullptr;
3219 }
3220 return rec.fHandle;
3221}
3222
3223std::unique_ptr<SkCanvas>
3224SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3225 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003226 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003227 return nullptr;
3228 }
3229
3230 SkBitmap bm;
3231 Handle hndl;
3232
3233 if (rec) {
3234 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3235 } else {
3236 hndl = alloc->allocBitmap(info, &bm);
3237 }
3238 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3239}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003240
3241///////////////////////////////////////////////////////////////////////////////////////////////////