blob: 3ed8f1349c11a24ecbe1ddab30fb5239f90684f9 [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) {
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000497 fAllowSimplifyClip = false;
reed2ff1fce2014-12-11 07:07:37 -0800498 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500501 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500502 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700503 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reeda499f902015-05-01 09:34:31 -0700505 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
506 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400507 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510
halcanary96fcdcc2015-08-27 07:41:13 -0700511 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000512
reedf92c8662014-08-18 08:02:43 -0700513 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700514 // The root device and the canvas should always have the same pixel geometry
515 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800516 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700517 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518
Mike Reedc42a1cd2017-02-14 14:25:14 -0500519 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700520 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400521
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500522 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
reed@google.comcde92112011-07-06 20:00:52 +0000525SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000526 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700527 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000528{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000529 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000530
Hal Canary363a3f82018-10-04 11:04:48 -0400531 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000532}
533
reed96a857e2015-01-25 10:33:58 -0800534SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000535 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800536 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537{
538 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400539 this->init(sk_make_sp<SkNoPixelsDevice>(
Hal Canary363a3f82018-10-04 11:04:48 -0400540 SkIRect::MakeWH(SkTMax(width, 0), SkTMax(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700541}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000542
Hal Canary363a3f82018-10-04 11:04:48 -0400543SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700544 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700545 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700546{
547 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700548
Mike Reed566e53c2017-03-10 10:49:45 -0500549 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400550 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700551}
552
Herb Derbyefe39bc2018-05-01 17:06:20 -0400553SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000554 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700555 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000556{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700558
Hal Canary363a3f82018-10-04 11:04:48 -0400559 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700560}
561
reed4a8126e2014-09-22 07:29:03 -0700562SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700563 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700564 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700565{
566 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700567
Mike Reed910ca0f2018-04-25 13:04:05 -0400568 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400569 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700570}
reed29c857d2014-09-21 10:25:07 -0700571
Mike Reed356f7c22017-01-10 11:58:39 -0500572SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
573 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700574 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
575 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500576 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700577{
578 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700579
Mike Reed910ca0f2018-04-25 13:04:05 -0400580 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400581 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582}
583
Mike Reed356f7c22017-01-10 11:58:39 -0500584SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
585
Matt Sarett31f99ce2017-04-11 08:46:01 -0400586#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
587SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
588 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
589 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
590 , fAllocator(nullptr)
591{
592 inc_canvas();
593
594 SkBitmap tmp(bitmap);
595 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400596 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400597 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400598}
599#endif
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601SkCanvas::~SkCanvas() {
602 // free up the contents of our deque
603 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 this->internalRestore(); // restore the last, since we're going away
606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 dec_canvas();
608}
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610///////////////////////////////////////////////////////////////////////////////
611
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000612void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700613 this->onFlush();
614}
615
616void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000617 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000618 if (device) {
619 device->flush();
620 }
621}
622
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500623SkSurface* SkCanvas::getSurface() const {
624 return fSurfaceBase;
625}
626
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000627SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000628 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000629 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
630}
631
senorblancoafc7cce2016-02-02 18:44:15 -0800632SkIRect SkCanvas::getTopLayerBounds() const {
633 SkBaseDevice* d = this->getTopDevice();
634 if (!d) {
635 return SkIRect::MakeEmpty();
636 }
Michael Ludwig915b7792019-10-22 17:40:41 +0000637 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
senorblancoafc7cce2016-02-02 18:44:15 -0800638}
639
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000640SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000642 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400644 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645}
646
Florin Malita0ed3b642017-01-13 16:56:38 +0000647SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400648 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000649}
650
Mike Reed353196f2017-07-21 11:01:18 -0400651bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000652 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400653 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000654}
655
Mike Reed353196f2017-07-21 11:01:18 -0400656bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
657 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400658}
659
660bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
661 SkPixmap pm;
662 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
663}
664
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000665bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400666 SkPixmap pm;
667 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700668 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000669 }
670 return false;
671}
672
Matt Sarett03dd6d52017-01-23 12:15:09 -0500673bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000675 SkBaseDevice* device = this->getDevice();
676 if (!device) {
677 return false;
678 }
679
Matt Sarett03dd6d52017-01-23 12:15:09 -0500680 // This check gives us an early out and prevents generation ID churn on the surface.
681 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
682 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400683 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500684 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000686
Matt Sarett03dd6d52017-01-23 12:15:09 -0500687 // Tell our owning surface to bump its generation ID.
688 const bool completeOverwrite =
689 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700690 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700691
Matt Sarett03dd6d52017-01-23 12:15:09 -0500692 // This can still fail, most notably in the case of a invalid color type or alpha type
693 // conversion. We could pull those checks into this function and avoid the unnecessary
694 // generation ID bump. But then we would be performing those checks twice, since they
695 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400696 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000697}
reed@google.com51df9e32010-12-23 19:29:18 +0000698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699//////////////////////////////////////////////////////////////////////////////
700
reed2ff1fce2014-12-11 07:07:37 -0800701void SkCanvas::checkForDeferredSave() {
702 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800703 this->doSave();
704 }
705}
706
reedf0090cb2014-11-26 08:55:51 -0800707int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800708#ifdef SK_DEBUG
709 int count = 0;
710 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
711 for (;;) {
712 const MCRec* rec = (const MCRec*)iter.next();
713 if (!rec) {
714 break;
715 }
716 count += 1 + rec->fDeferredSaveCount;
717 }
718 SkASSERT(count == fSaveCount);
719#endif
720 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800721}
722
723int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800724 fSaveCount += 1;
725 fMCRec->fDeferredSaveCount += 1;
726 return this->getSaveCount() - 1; // return our prev value
727}
728
729void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800730 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700731
732 SkASSERT(fMCRec->fDeferredSaveCount > 0);
733 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800734 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800735}
736
737void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800738 if (fMCRec->fDeferredSaveCount > 0) {
739 SkASSERT(fSaveCount > 1);
740 fSaveCount -= 1;
741 fMCRec->fDeferredSaveCount -= 1;
742 } else {
743 // check for underflow
744 if (fMCStack.count() > 1) {
745 this->willRestore();
746 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700747 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800748 this->internalRestore();
749 this->didRestore();
750 }
reedf0090cb2014-11-26 08:55:51 -0800751 }
752}
753
754void SkCanvas::restoreToCount(int count) {
755 // sanity check
756 if (count < 1) {
757 count = 1;
758 }
mtkleinf0f14112014-12-12 08:46:25 -0800759
reedf0090cb2014-11-26 08:55:51 -0800760 int n = this->getSaveCount() - count;
761 for (int i = 0; i < n; ++i) {
762 this->restore();
763 }
764}
765
reed2ff1fce2014-12-11 07:07:37 -0800766void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700768 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000770
Mike Reedc42a1cd2017-02-14 14:25:14 -0500771 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772}
773
reed4960eee2015-12-18 07:09:18 -0800774bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400775 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776}
777
reed4960eee2015-12-18 07:09:18 -0800778bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700779 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400780 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
781 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
782 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
783 // filter does not require a decomposed CTM matrix, the filter space and device space are the
784 // same. When it has been decomposed, we want the original image filter node to process the
785 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
786 // longer the original filter, but has the remainder matrix baked into it, and passing in the
787 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
788 // to the original filter node (barring inflation from consecutive calls to mapRect). While
789 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
790 // passing getDeviceClipBounds() to 'imageFilter' is correct.
791 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
792 // be important to more accurately calculate the clip bounds in the layer space for the original
793 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500794 SkIRect clipBounds = this->getDeviceClipBounds();
795 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000796 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000797 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000798
reed96e657d2015-03-10 17:30:07 -0700799 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
800
Robert Phillips12078432018-05-17 11:17:39 -0400801 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
802 // If the image filter DAG affects transparent black then we will need to render
803 // out to the clip bounds
804 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000805 }
Robert Phillips12078432018-05-17 11:17:39 -0400806
807 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700808 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700810 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400811 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400813 inputSaveLayerBounds = clipBounds;
814 }
815
816 if (imageFilter) {
817 // expand the clip bounds by the image filter DAG to include extra content that might
818 // be required by the image filters.
819 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
820 SkImageFilter::kReverse_MapDirection,
821 &inputSaveLayerBounds);
822 }
823
824 SkIRect clippedSaveLayerBounds;
825 if (bounds) {
826 // For better or for worse, user bounds currently act as a hard clip on the layer's
827 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
828 clippedSaveLayerBounds = inputSaveLayerBounds;
829 } else {
830 // If there are no user bounds, we don't want to artificially restrict the resulting
831 // layer bounds, so allow the expanded clip bounds free reign.
832 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800834
835 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400836 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800837 if (BoundsAffectsClip(saveLayerFlags)) {
838 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
839 fMCRec->fRasterClip.setEmpty();
840 fDeviceClipBounds.setEmpty();
841 }
842 return false;
843 }
Robert Phillips12078432018-05-17 11:17:39 -0400844 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845
reed4960eee2015-12-18 07:09:18 -0800846 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700847 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400848 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
849 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000850 }
851
852 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400853 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000854 }
Robert Phillips12078432018-05-17 11:17:39 -0400855
junov@chromium.orga907ac32012-02-24 21:54:07 +0000856 return true;
857}
858
reed4960eee2015-12-18 07:09:18 -0800859int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
860 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000861}
862
Cary Clarke041e312018-03-06 13:00:52 -0500863int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700864 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400865 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
866 // no need for the layer (or any of the draws until the matching restore()
867 this->save();
868 this->clipRect({0,0,0,0});
869 } else {
870 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
871 fSaveCount += 1;
872 this->internalSaveLayer(rec, strategy);
873 }
reed4960eee2015-12-18 07:09:18 -0800874 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800875}
876
Mike Reed148b7fd2018-12-18 17:38:18 -0500877int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
878 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
879 // Assuming clips never expand, if the request bounds is outside of the current clip
880 // there is no need to copy/restore the area, so just devolve back to a regular save.
881 this->save();
882 } else {
883 bool doTheWork = this->onDoSaveBehind(bounds);
884 fSaveCount += 1;
885 this->internalSave();
886 if (doTheWork) {
887 this->internalSaveBehind(bounds);
888 }
889 }
890 return this->getSaveCount() - 1;
891}
892
reeda2217ef2016-07-20 06:04:34 -0700893void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500894 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500895 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400896 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
897 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400898 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig915b7792019-10-22 17:40:41 +0000899
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400900 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400901 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
902 // This means that we only have to copy a dst-sized block of pixels out of src and translate
903 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400904 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
905 dstOrigin.y() - src->getOrigin().y(),
906 dst->width(), dst->height());
907 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400908 return;
909 }
910
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400911 auto special = src->snapSpecial(snapBounds);
912 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400913 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
914 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400915 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
916 }
917 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400918 }
919
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400920 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
921 // by the backdrop filter.
922 SkMatrix toRoot, layerMatrix;
923 SkSize scale;
924 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
925 toRoot = SkMatrix::I();
926 layerMatrix = ctm;
927 } else if (ctm.decomposeScale(&scale, &toRoot)) {
928 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
929 } else {
930 // Perspective, for now, do no scaling of the layer itself.
931 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
932 // the matrix, e.g. based on the midpoint of the near/far planes?
933 toRoot = ctm;
934 layerMatrix = SkMatrix::I();
935 }
936
937 // We have to map the dst bounds from the root space into the layer space where filtering will
938 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
939 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
940 // is a safe, conservative estimate.
941 SkMatrix fromRoot;
942 if (!toRoot.invert(&fromRoot)) {
943 return;
944 }
945
946 // This represents what the backdrop filter needs to produce in the layer space, and is sized
947 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
948 SkIRect layerTargetBounds = fromRoot.mapRect(
949 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
950 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
951 // require some extra input pixels.
952 SkIRect layerInputBounds = filter->filterBounds(
953 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
954 &layerTargetBounds);
955
956 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400957 // be the conservative contents required to fill a layerInputBounds-sized surface with the
958 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400959 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
960 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
961 if (!backdropBounds.intersect(srcDevRect)) {
962 return;
963 }
964
965 auto special = src->snapSpecial(backdropBounds);
966 if (!special) {
967 return;
968 }
969
970 SkColorType colorType = src->imageInfo().colorType();
971 if (colorType == kUnknown_SkColorType) {
972 colorType = kRGBA_8888_SkColorType;
973 }
974 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400975
976 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400977 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400978 // Drawing the temporary and final filtered image requires a higher filter quality if the
979 // 'toRoot' transformation is not identity, in order to minimize the impact on already
980 // rendered edges/content.
981 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
982 p.setFilterQuality(kHigh_SkFilterQuality);
983
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400984 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
985 // and stored in a temporary surface, which is then used as the input to the actual filter.
986 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
987 if (!tmpSurface) {
988 return;
989 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400990
991 auto tmpCanvas = tmpSurface->getCanvas();
992 tmpCanvas->clear(SK_ColorTRANSPARENT);
993 // Reading in reverse, this takes the backdrop bounds from src device space into the root
994 // space, then maps from root space into the layer space, then maps it so the input layer's
995 // top left corner is (0, 0). This transformation automatically accounts for any cropping
996 // performed on backdropBounds.
997 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
998 tmpCanvas->concat(fromRoot);
999 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001000
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001001 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1002 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1003 special = tmpSurface->makeImageSnapshot();
1004 } else {
1005 // Since there is no extra transform that was done, update the input bounds to reflect
1006 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1007 // was equal to backdropBounds before it was made relative to the src device and cropped.
1008 // When we use the original snapped image directly, just map the update backdrop bounds
1009 // back into the shared layer space
1010 layerInputBounds = backdropBounds;
1011 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001012
1013 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1014 // draw will be 1-1 so there is no need to increase filter quality.
1015 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001016 }
1017
1018 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1019 // layer space. This has to further offset everything so that filter evaluation thinks the
1020 // source image's top left corner is (0, 0).
1021 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1022 // this can be simplified.
1023 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1024 SkMatrix filterCTM = layerMatrix;
1025 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1026 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1027
1028 SkIPoint offset;
1029 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001030 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001031 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1032 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1033 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1034 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001035 offset += layerInputBounds.topLeft();
1036
1037 // Manually setting the device's CTM requires accounting for the device's origin.
1038 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001039 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001040 // a global CTM instead of a device CTM.
1041 SkMatrix dstCTM = toRoot;
1042 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001043 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001044
1045 // And because devices don't have a special-image draw function that supports arbitrary
1046 // matrices, we are abusing the asImage() functionality here...
1047 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001048 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001049 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001050 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001051 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1052 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001053 }
robertphillips7354a4b2015-12-16 05:08:27 -08001054}
reed70ee31b2015-12-10 13:44:45 -08001055
Mike Kleine083f7c2018-02-07 12:54:27 -05001056static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001057 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001058 if (prev.bytesPerPixel() <= 4 &&
1059 prev.colorType() != kRGBA_8888_SkColorType &&
1060 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001061 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1062 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1063 ct = kN32_SkColorType;
1064 }
1065 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001066}
1067
reed4960eee2015-12-18 07:09:18 -08001068void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001069 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001070 const SkRect* bounds = rec.fBounds;
1071 const SkPaint* paint = rec.fPaint;
1072 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1073
Mike Reed5532c2a2019-02-23 12:00:32 -05001074 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1075 // regardless of any hint-rect from the caller. skbug.com/8783
1076 if (rec.fBackdrop) {
1077 bounds = nullptr;
1078 }
1079
reed8c30a812016-04-20 16:36:51 -07001080 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001081 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001082 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001083 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001084
reed8c30a812016-04-20 16:36:51 -07001085 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001086 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1087 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1088 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001089 *
1090 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001091 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1092 * if necessary.
1093 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1094 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001095 * 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 -04001096 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001097 * of the original imagefilter, and draw that (via drawSprite)
1098 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1099 *
1100 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1101 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1102 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001103 if (imageFilter) {
1104 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001105 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1106 &modifiedCTM);
1107 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001108 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001109 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001110 modifiedRec = fMCRec;
1111 this->internalSetMatrix(modifiedCTM);
1112 SkPaint* p = lazyP.set(*paint);
1113 p->setImageFilter(std::move(modifiedFilter));
1114 imageFilter = p->getImageFilter();
1115 paint = p;
1116 }
1117 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1118 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001119 }
reed8c30a812016-04-20 16:36:51 -07001120
junov@chromium.orga907ac32012-02-24 21:54:07 +00001121 // do this before we create the layer. We don't call the public save() since
1122 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001123 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001124
junov@chromium.orga907ac32012-02-24 21:54:07 +00001125 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001126 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001127 if (modifiedRec) {
1128 // In this case there will be no layer in which to stash the matrix so we need to
1129 // revert the prior MCRec to its earlier state.
1130 modifiedRec->fMatrix = stashedMatrix;
1131 }
reed2ff1fce2014-12-11 07:07:37 -08001132 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 }
1134
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001135 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1136 // the clipRectBounds() call above?
1137 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001138 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001139 }
1140
reed8dc0ccb2015-03-20 06:32:52 -07001141 SkPixelGeometry geo = fProps.pixelGeometry();
1142 if (paint) {
reed76033be2015-03-14 10:54:31 -07001143 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001144 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001145 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001146 }
1147 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148
robertphillips5139e502016-07-19 05:10:40 -07001149 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001150 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001151 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001152 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001153 }
reedb2db8982014-11-13 12:41:02 -08001154
Mike Kleine083f7c2018-02-07 12:54:27 -05001155 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001156 if (rec.fSaveLayerFlags & kF16ColorType) {
1157 info = info.makeColorType(kRGBA_F16_SkColorType);
1158 }
reed129ed1c2016-02-22 06:42:31 -08001159
Hal Canary704cd322016-11-07 14:13:52 -05001160 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001161 {
Florin Malita4571e492019-07-16 10:25:58 -04001162 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001163 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001164 const bool trackCoverage =
1165 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001166 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001167 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001168 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001169 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1170 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001171 return;
reed61f501f2015-04-29 08:34:00 -07001172 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001173 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001174 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175
Mike Reedb43a3e02017-02-11 10:18:58 -05001176 // only have a "next" if this new layer doesn't affect the clip (rare)
1177 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 fMCRec->fLayer = layer;
1179 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001180
Mike Reedc61abee2017-02-28 17:45:27 -05001181 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001182 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001183 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001184 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001185
Mike Reedc42a1cd2017-02-14 14:25:14 -05001186 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1187
1188 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1189 if (layer->fNext) {
1190 // need to punch a hole in the previous device, so we don't draw there, given that
1191 // the new top-layer will allow drawing to happen "below" it.
1192 SkRegion hole(ir);
1193 do {
1194 layer = layer->fNext;
1195 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1196 } while (layer->fNext);
1197 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001200int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001201 if (0xFF == alpha) {
1202 return this->saveLayer(bounds, nullptr);
1203 } else {
1204 SkPaint tmpPaint;
1205 tmpPaint.setAlpha(alpha);
1206 return this->saveLayer(bounds, &tmpPaint);
1207 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001208}
1209
Mike Reed148b7fd2018-12-18 17:38:18 -05001210void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001211 SkIRect devBounds;
1212 if (localBounds) {
1213 SkRect tmp;
1214 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1215 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1216 devBounds.setEmpty();
1217 }
1218 } else {
1219 devBounds = this->getDeviceClipBounds();
1220 }
1221 if (devBounds.isEmpty()) {
1222 return;
1223 }
1224
Mike Reed148b7fd2018-12-18 17:38:18 -05001225 SkBaseDevice* device = this->getTopDevice();
1226 if (nullptr == device) { // Do we still need this check???
1227 return;
1228 }
1229
Michael Ludwig915b7792019-10-22 17:40:41 +00001230 // need the bounds relative to the device itself
1231 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
Mike Reed148b7fd2018-12-18 17:38:18 -05001232
Michael Ludwigac352122019-08-28 21:03:05 +00001233 // This is getting the special image from the current device, which is then drawn into (both by
1234 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1235 // own device, we need to explicitly copy the back image contents so that its original content
1236 // is available when we splat it back later during restore.
1237 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001238 if (!backImage) {
1239 return;
1240 }
1241
1242 // we really need the save, so we can wack the fMCRec
1243 this->checkForDeferredSave();
1244
1245 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1246
1247 SkPaint paint;
1248 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001249 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001250}
1251
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252void SkCanvas::internalRestore() {
1253 SkASSERT(fMCStack.count() != 0);
1254
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001255 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 DeviceCM* layer = fMCRec->fLayer; // may be null
1257 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001258 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259
Mike Reed148b7fd2018-12-18 17:38:18 -05001260 // move this out before we do the actual restore
1261 auto backImage = std::move(fMCRec->fBackImage);
1262
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 // now do the normal restore()
1264 fMCRec->~MCRec(); // balanced in save()
1265 fMCStack.pop_back();
1266 fMCRec = (MCRec*)fMCStack.back();
1267
Mike Reedc42a1cd2017-02-14 14:25:14 -05001268 if (fMCRec) {
1269 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1270 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001271
Mike Reed148b7fd2018-12-18 17:38:18 -05001272 if (backImage) {
1273 SkPaint paint;
1274 paint.setBlendMode(SkBlendMode::kDstOver);
1275 const int x = backImage->fLoc.x();
1276 const int y = backImage->fLoc.y();
1277 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1278 nullptr, SkMatrix::I());
1279 }
1280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1282 since if we're being recorded, we don't want to record this (the
1283 recorder will have already recorded the restore).
1284 */
bsalomon49f085d2014-09-05 13:34:00 -07001285 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001286 if (fMCRec) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001287 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001288 layer->fDevice->setImmutable();
Michael Ludwig915b7792019-10-22 17:40:41 +00001289 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
1290 layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001291 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001292 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001293 this->internalSetMatrix(layer->fStashedMatrix);
Michael Ludwig915b7792019-10-22 17:40:41 +00001294 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001295 delete layer;
reedb679ca82015-04-07 04:40:48 -07001296 } else {
1297 // we're at the root
reeda499f902015-05-01 09:34:31 -07001298 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001299 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001300 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001302 }
msarettfbfa2582016-08-12 08:29:08 -07001303
1304 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001305 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001306 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1307 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308}
1309
reede8f30622016-03-23 18:59:25 -07001310sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001311 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001312 props = &fProps;
1313 }
1314 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001315}
1316
reede8f30622016-03-23 18:59:25 -07001317sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001318 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001319 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001320}
1321
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001322SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001323 return this->onImageInfo();
1324}
1325
1326SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001327 SkBaseDevice* dev = this->getDevice();
1328 if (dev) {
1329 return dev->imageInfo();
1330 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001331 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001332 }
1333}
1334
brianosman898235c2016-04-06 07:38:23 -07001335bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001336 return this->onGetProps(props);
1337}
1338
1339bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001340 SkBaseDevice* dev = this->getDevice();
1341 if (dev) {
1342 if (props) {
1343 *props = fProps;
1344 }
1345 return true;
1346 } else {
1347 return false;
1348 }
1349}
1350
reed6ceeebd2016-03-09 14:26:26 -08001351bool SkCanvas::peekPixels(SkPixmap* pmap) {
1352 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001353}
1354
reed884e97c2015-05-26 11:31:54 -07001355bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001356 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001357 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001358}
1359
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001360void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001361 SkPixmap pmap;
1362 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001363 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001364 }
1365 if (info) {
1366 *info = pmap.info();
1367 }
1368 if (rowBytes) {
1369 *rowBytes = pmap.rowBytes();
1370 }
1371 if (origin) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001372 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001373 }
reed884e97c2015-05-26 11:31:54 -07001374 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001375}
1376
reed884e97c2015-05-26 11:31:54 -07001377bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001378 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001379 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001380}
1381
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383
Mike Reed8bcd1282019-03-13 16:51:54 -04001384// In our current design/features, we should never have a layer (src) in a different colorspace
1385// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1386// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1387// colorspace.
1388static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1389 SkASSERT(src == dst);
1390}
1391
Michael Ludwig915b7792019-10-22 17:40:41 +00001392void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001393 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001395 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 paint = &tmp;
1397 }
reed@google.com4b226022011-01-11 18:32:13 +00001398
Mike Reed38992392019-07-30 10:48:15 -04001399 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001402 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001403 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1404 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001405 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001406 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwig915b7792019-10-22 17:40:41 +00001407 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001408 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001409 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1410 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001411 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1412 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001413 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1414 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001415 }
reed@google.com76dd2772012-01-05 21:15:07 +00001416 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001417 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001418 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 }
reeda2217ef2016-07-20 06:04:34 -07001420
Mike Reed38992392019-07-30 10:48:15 -04001421 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422}
1423
reed32704672015-12-16 08:27:10 -08001424/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001425
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001426void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001427 if (dx || dy) {
1428 this->checkForDeferredSave();
reedfe69b502016-09-12 06:31:48 -07001429 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001430
reedfe69b502016-09-12 06:31:48 -07001431 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001432 // However, if either is non-finite, we might still complicate the matrix type,
1433 // so we still have to compute this.
1434 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001435
Mike Reedc42a1cd2017-02-14 14:25:14 -05001436 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001437
reedfe69b502016-09-12 06:31:48 -07001438 this->didTranslate(dx,dy);
1439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440}
1441
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001442void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001443 SkMatrix m;
1444 m.setScale(sx, sy);
1445 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446}
1447
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001448void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001449 SkMatrix m;
1450 m.setRotate(degrees);
1451 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452}
1453
bungeman7438bfc2016-07-12 15:01:19 -07001454void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1455 SkMatrix m;
1456 m.setRotate(degrees, px, py);
1457 this->concat(m);
1458}
1459
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001460void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001461 SkMatrix m;
1462 m.setSkew(sx, sy);
1463 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001464}
1465
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001466void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001467 if (matrix.isIdentity()) {
1468 return;
1469 }
1470
reed2ff1fce2014-12-11 07:07:37 -08001471 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001472 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001473 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001474
Mike Reed7627fa52017-02-08 10:07:53 -05001475 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001476
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001477 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001478}
1479
Mike Reed403c8072020-01-08 10:40:39 -05001480#ifndef SK_SUPPORT_LEGACY_CANVAS_MATRIX_33
1481// inefficient, just wanted something so we can test with for now
1482void SkCanvas::concat(const SkMatrix44& matrix) {
1483 this->checkForDeferredSave();
1484
1485 SkScalar m[16];
1486 matrix.asColMajorf(m);
1487 SkM44 m44;
1488 m44.setColMajor(m);
1489 fMCRec->fMatrix.preConcat(m44);
1490
1491 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1492
1493 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1494
1495 this->didConcat44(m);
1496}
1497#endif
1498
reed8c30a812016-04-20 16:36:51 -07001499void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001500 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001501 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001502
Mike Reedc42a1cd2017-02-14 14:25:14 -05001503 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001504}
1505
1506void SkCanvas::setMatrix(const SkMatrix& matrix) {
1507 this->checkForDeferredSave();
1508 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001509 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510}
1511
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001513 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
1516//////////////////////////////////////////////////////////////////////////////
1517
Mike Reedc1f77742016-12-09 09:00:50 -05001518void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001519 if (!rect.isFinite()) {
1520 return;
1521 }
reed2ff1fce2014-12-11 07:07:37 -08001522 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001523 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1524 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001525}
1526
Mike Reedc1f77742016-12-09 09:00:50 -05001527void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001528 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001529
Mike Reed7627fa52017-02-08 10:07:53 -05001530 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001531
reedc64eff52015-11-21 12:39:45 -08001532 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001533 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1534 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001535 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536}
1537
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001538void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1539 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001540 if (fClipRestrictionRect.isEmpty()) {
1541 // we notify the device, but we *dont* resolve deferred saves (since we're just
1542 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001543 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001544 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001545 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001546 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001547 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001548 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001549 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1550 }
1551}
1552
Mike Reedc1f77742016-12-09 09:00:50 -05001553void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001554 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001555 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001556 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001557 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1558 } else {
1559 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001560 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001562
Mike Reedc1f77742016-12-09 09:00:50 -05001563void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001564 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001565
Brian Salomona3b45d42016-10-03 11:36:16 -04001566 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001567
Mike Reed7627fa52017-02-08 10:07:53 -05001568 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001569
Mike Reed20800c82017-11-15 16:09:04 -05001570 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1571 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001572 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001573}
1574
Mike Reedc1f77742016-12-09 09:00:50 -05001575void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001576 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001577 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001578
1579 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1580 SkRect r;
1581 if (path.isRect(&r)) {
1582 this->onClipRect(r, op, edgeStyle);
1583 return;
1584 }
1585 SkRRect rrect;
1586 if (path.isOval(&r)) {
1587 rrect.setOval(r);
1588 this->onClipRRect(rrect, op, edgeStyle);
1589 return;
1590 }
1591 if (path.isRRect(&rrect)) {
1592 this->onClipRRect(rrect, op, edgeStyle);
1593 return;
1594 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595 }
robertphillips39f05382015-11-24 09:30:12 -08001596
1597 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598}
1599
Mike Reedc1f77742016-12-09 09:00:50 -05001600void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001601 AutoValidateClip avc(this);
1602
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001604
Mike Reed7627fa52017-02-08 10:07:53 -05001605 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606
Brian Salomona3b45d42016-10-03 11:36:16 -04001607 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001608 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001609 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001610 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611}
1612
Mike Reedc1f77742016-12-09 09:00:50 -05001613void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001614 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001615 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616}
1617
Mike Reedc1f77742016-12-09 09:00:50 -05001618void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001619 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001620
reed@google.com5c3d1472011-02-22 19:12:23 +00001621 AutoValidateClip avc(this);
1622
Mike Reed20800c82017-11-15 16:09:04 -05001623 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001624 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625}
1626
reed@google.com819c9212011-02-23 18:56:55 +00001627#ifdef SK_DEBUG
1628void SkCanvas::validateClip() const {
1629 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001630 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001631 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001632 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001633 return;
1634 }
reed@google.com819c9212011-02-23 18:56:55 +00001635}
1636#endif
1637
Mike Reeda1361362017-03-07 09:37:29 -05001638bool SkCanvas::androidFramework_isClipAA() const {
1639 bool containsAA = false;
1640
1641 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1642
1643 return containsAA;
1644}
1645
1646class RgnAccumulator {
1647 SkRegion* fRgn;
1648public:
1649 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1650 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1651 SkIPoint origin = device->getOrigin();
1652 if (origin.x() | origin.y()) {
1653 rgn->translate(origin.x(), origin.y());
1654 }
1655 fRgn->op(*rgn, SkRegion::kUnion_Op);
1656 }
1657};
1658
1659void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1660 RgnAccumulator accum(rgn);
1661 SkRegion tmp;
1662
1663 rgn->setEmpty();
1664 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001665}
1666
reed@google.com5c3d1472011-02-22 19:12:23 +00001667///////////////////////////////////////////////////////////////////////////////
1668
reed@google.com754de5f2014-02-24 19:38:20 +00001669bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001670 return fMCRec->fRasterClip.isEmpty();
1671
1672 // TODO: should we only use the conservative answer in a recording canvas?
1673#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001674 SkBaseDevice* dev = this->getTopDevice();
1675 // if no device we return true
1676 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001677#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001678}
1679
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001680bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001681 SkBaseDevice* dev = this->getTopDevice();
1682 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001683 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001684}
1685
msarettfbfa2582016-08-12 08:29:08 -07001686static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1687#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1688 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1689 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1690 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1691 return 0xF != _mm_movemask_ps(mask);
1692#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1693 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1694 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1695 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1696 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1697#else
1698 SkRect devRectAsRect;
1699 SkRect devClipAsRect;
1700 devRect.store(&devRectAsRect.fLeft);
1701 devClip.store(&devClipAsRect.fLeft);
1702 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1703#endif
1704}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001705
msarettfbfa2582016-08-12 08:29:08 -07001706// It's important for this function to not be inlined. Otherwise the compiler will share code
1707// between the fast path and the slow path, resulting in two slow paths.
1708static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1709 const SkMatrix& matrix) {
1710 SkRect deviceRect;
1711 matrix.mapRect(&deviceRect, src);
1712 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1713}
1714
1715bool SkCanvas::quickReject(const SkRect& src) const {
1716#ifdef SK_DEBUG
1717 // Verify that fDeviceClipBounds are set properly.
1718 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001719 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001720 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001721 } else {
msarettfbfa2582016-08-12 08:29:08 -07001722 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 }
msarettfbfa2582016-08-12 08:29:08 -07001724
msarett9637ea92016-08-18 14:03:30 -07001725 // Verify that fIsScaleTranslate is set properly.
1726 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001727#endif
1728
msarett9637ea92016-08-18 14:03:30 -07001729 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001730 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1731 }
1732
1733 // We inline the implementation of mapScaleTranslate() for the fast path.
1734 float sx = fMCRec->fMatrix.getScaleX();
1735 float sy = fMCRec->fMatrix.getScaleY();
1736 float tx = fMCRec->fMatrix.getTranslateX();
1737 float ty = fMCRec->fMatrix.getTranslateY();
1738 Sk4f scale(sx, sy, sx, sy);
1739 Sk4f trans(tx, ty, tx, ty);
1740
1741 // Apply matrix.
1742 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1743
1744 // Make sure left < right, top < bottom.
1745 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1746 Sk4f min = Sk4f::Min(ltrb, rblt);
1747 Sk4f max = Sk4f::Max(ltrb, rblt);
1748 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1749 // ARM this sequence generates the fastest (a single instruction).
1750 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1751
1752 // Check if the device rect is NaN or outside the clip.
1753 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754}
1755
reed@google.com3b3e8952012-08-16 20:53:31 +00001756bool SkCanvas::quickReject(const SkPath& path) const {
1757 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758}
1759
Mike Klein83c8dd92017-11-28 17:08:45 -05001760SkRect SkCanvas::getLocalClipBounds() const {
1761 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001762 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001763 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 }
1765
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001766 SkMatrix inverse;
1767 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001768 if (!fMCRec->fMatrix.invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001769 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001770 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771
Mike Reed42e8c532017-01-23 14:09:13 -05001772 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001773 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001774 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001775
Mike Reedb57b9312018-04-23 12:12:54 -04001776 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001777 inverse.mapRect(&bounds, r);
1778 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779}
1780
Mike Klein83c8dd92017-11-28 17:08:45 -05001781SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001782 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001783}
1784
Mike Reed403c8072020-01-08 10:40:39 -05001785SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001786 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787}
1788
Brian Osman11052242016-10-27 14:47:55 -04001789GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001790 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001791 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001792}
1793
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001794GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001795 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001796 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001797}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001798
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001799void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1800 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001801 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001802 if (outer.isEmpty()) {
1803 return;
1804 }
1805 if (inner.isEmpty()) {
1806 this->drawRRect(outer, paint);
1807 return;
1808 }
1809
1810 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001811 // be able to return ...
1812 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001813 //
1814 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001815 if (!outer.getBounds().contains(inner.getBounds())) {
1816 return;
1817 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001818
1819 this->onDrawDRRect(outer, inner, paint);
1820}
1821
reed41af9662015-01-05 07:49:08 -08001822void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001823 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001824 this->onDrawPaint(paint);
1825}
1826
1827void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001828 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001829 // To avoid redundant logic in our culling code and various backends, we always sort rects
1830 // before passing them along.
1831 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001832}
1833
Mike Reedd5674082019-04-19 15:00:47 -04001834void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1835 TRACE_EVENT0("skia", TRACE_FUNC);
1836 this->onDrawBehind(paint);
1837}
1838
msarettdca352e2016-08-26 06:37:45 -07001839void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001840 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001841 if (region.isEmpty()) {
1842 return;
1843 }
1844
1845 if (region.isRect()) {
1846 return this->drawIRect(region.getBounds(), paint);
1847 }
1848
1849 this->onDrawRegion(region, paint);
1850}
1851
reed41af9662015-01-05 07:49:08 -08001852void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001853 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001854 // To avoid redundant logic in our culling code and various backends, we always sort rects
1855 // before passing them along.
1856 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001857}
1858
1859void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001860 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001861 this->onDrawRRect(rrect, paint);
1862}
1863
1864void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001865 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001866 this->onDrawPoints(mode, count, pts, paint);
1867}
1868
Mike Reede88a1cb2017-03-17 09:50:46 -04001869void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1870 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001871 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001872 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001873 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1874 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001875 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001876}
1877
1878void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001879 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001880 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001881 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1882}
1883
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001884void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1885 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001886 TRACE_EVENT0("skia", TRACE_FUNC);
1887 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001888 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001889 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1890}
1891
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001892void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1893 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001894 TRACE_EVENT0("skia", TRACE_FUNC);
1895 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001896 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001897 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001898}
1899
1900void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001901 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001902 this->onDrawPath(path, paint);
1903}
1904
reeda85d4d02015-05-06 12:56:48 -07001905void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001906 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001907 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001908 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001909}
1910
Mike Reedc4e31092018-01-30 11:15:27 -05001911// Returns true if the rect can be "filled" : non-empty and finite
1912static bool fillable(const SkRect& r) {
1913 SkScalar w = r.width();
1914 SkScalar h = r.height();
1915 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1916}
1917
reede47829b2015-08-06 10:02:53 -07001918void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1919 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001920 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001921 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001922 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001923 return;
1924 }
1925 this->onDrawImageRect(image, &src, dst, paint, constraint);
1926}
reed41af9662015-01-05 07:49:08 -08001927
reed84984ef2015-07-17 07:09:43 -07001928void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1929 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001930 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001931 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001932}
1933
Brian Salomonf08002c2018-10-26 16:15:46 -04001934void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001935 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001936 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001937 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001938}
reede47829b2015-08-06 10:02:53 -07001939
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001940namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001941class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001942public:
Brian Salomon969be1c2018-05-21 14:37:49 -04001943 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
1944 if (!origPaint) {
1945 return;
1946 }
1947 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
1948 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
1949 }
1950 if (origPaint->getMaskFilter()) {
1951 fPaint.writable()->setMaskFilter(nullptr);
1952 }
1953 if (origPaint->isAntiAlias()) {
1954 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001955 }
1956 }
1957
1958 const SkPaint* get() const {
1959 return fPaint;
1960 }
1961
1962private:
Brian Salomon969be1c2018-05-21 14:37:49 -04001963 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001964};
1965} // namespace
1966
reed4c21dc52015-06-25 12:32:03 -07001967void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1968 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001969 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001970 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001971 if (dst.isEmpty()) {
1972 return;
1973 }
msarett552bca92016-08-03 06:53:26 -07001974 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001975 LatticePaint latticePaint(paint);
1976 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07001977 } else {
reede47829b2015-08-06 10:02:53 -07001978 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001979 }
reed4c21dc52015-06-25 12:32:03 -07001980}
1981
msarett16882062016-08-16 09:31:08 -07001982void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1983 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001984 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07001985 RETURN_ON_NULL(image);
1986 if (dst.isEmpty()) {
1987 return;
1988 }
msarett71df2d72016-09-30 12:41:42 -07001989
1990 SkIRect bounds;
1991 Lattice latticePlusBounds = lattice;
1992 if (!latticePlusBounds.fBounds) {
1993 bounds = SkIRect::MakeWH(image->width(), image->height());
1994 latticePlusBounds.fBounds = &bounds;
1995 }
1996
1997 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001998 LatticePaint latticePaint(paint);
1999 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002000 } else {
2001 this->drawImageRect(image, dst, paint);
2002 }
2003}
2004
reed41af9662015-01-05 07:49:08 -08002005void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002006 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002007 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002008 return;
2009 }
reed41af9662015-01-05 07:49:08 -08002010 this->onDrawBitmap(bitmap, dx, dy, paint);
2011}
2012
reede47829b2015-08-06 10:02:53 -07002013void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002014 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002015 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002016 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002017 return;
2018 }
reede47829b2015-08-06 10:02:53 -07002019 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002020}
2021
reed84984ef2015-07-17 07:09:43 -07002022void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2023 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002024 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002025}
2026
reede47829b2015-08-06 10:02:53 -07002027void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2028 SrcRectConstraint constraint) {
2029 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2030 constraint);
2031}
reede47829b2015-08-06 10:02:53 -07002032
reed41af9662015-01-05 07:49:08 -08002033void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2034 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002035 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002036 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002037 return;
2038 }
msarett552bca92016-08-03 06:53:26 -07002039 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002040 LatticePaint latticePaint(paint);
2041 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002042 } else {
reeda5517e22015-07-14 10:54:12 -07002043 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002044 }
reed41af9662015-01-05 07:49:08 -08002045}
2046
msarettc573a402016-08-02 08:05:56 -07002047void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2048 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002049 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002050 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002051 return;
2052 }
msarett71df2d72016-09-30 12:41:42 -07002053
2054 SkIRect bounds;
2055 Lattice latticePlusBounds = lattice;
2056 if (!latticePlusBounds.fBounds) {
2057 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2058 latticePlusBounds.fBounds = &bounds;
2059 }
2060
2061 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002062 LatticePaint latticePaint(paint);
2063 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002064 } else {
msarett16882062016-08-16 09:31:08 -07002065 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002066 }
msarettc573a402016-08-02 08:05:56 -07002067}
2068
reed71c3c762015-06-24 10:29:17 -07002069void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002070 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002071 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002072 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002073 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002074 if (count <= 0) {
2075 return;
2076 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002077 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002078 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002079 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002080}
2081
reedf70b5312016-03-04 16:36:20 -08002082void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002083 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002084 if (key) {
2085 this->onDrawAnnotation(rect, key, value);
2086 }
2087}
2088
reede47829b2015-08-06 10:02:53 -07002089void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2090 const SkPaint* paint, SrcRectConstraint constraint) {
2091 if (src) {
2092 this->drawImageRect(image, *src, dst, paint, constraint);
2093 } else {
2094 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2095 dst, paint, constraint);
2096 }
2097}
2098void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2099 const SkPaint* paint, SrcRectConstraint constraint) {
2100 if (src) {
2101 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2102 } else {
2103 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2104 dst, paint, constraint);
2105 }
2106}
2107
Mike Reed4204da22017-05-17 08:53:36 -04002108void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002109 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002110 this->onDrawShadowRec(path, rec);
2111}
2112
2113void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2114 SkPaint paint;
2115 const SkRect& pathBounds = path.getBounds();
2116
Mike Reed38992392019-07-30 10:48:15 -04002117 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002118 while (iter.next()) {
2119 iter.fDevice->drawShadow(path, rec);
2120 }
Mike Reed38992392019-07-30 10:48:15 -04002121 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002122}
2123
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002124void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002125 QuadAAFlags aaFlags, const SkColor4f& color,
2126 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002127 TRACE_EVENT0("skia", TRACE_FUNC);
2128 // Make sure the rect is sorted before passing it along
2129 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2130}
2131
2132void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2133 const SkPoint dstClips[],
2134 const SkMatrix preViewMatrices[],
2135 const SkPaint* paint,
2136 SrcRectConstraint constraint) {
2137 TRACE_EVENT0("skia", TRACE_FUNC);
2138 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2139}
2140
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141//////////////////////////////////////////////////////////////////////////////
2142// These are the virtual drawing methods
2143//////////////////////////////////////////////////////////////////////////////
2144
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002145void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002146 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002147 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2148 }
2149}
2150
reed41af9662015-01-05 07:49:08 -08002151void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002152 this->internalDrawPaint(paint);
2153}
2154
2155void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002156 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157
2158 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002159 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 }
2161
Mike Reed38992392019-07-30 10:48:15 -04002162 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002163}
2164
reed41af9662015-01-05 07:49:08 -08002165void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2166 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 if ((long)count <= 0) {
2168 return;
2169 }
2170
Mike Reed822128b2017-02-28 16:41:03 -05002171 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002172 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002173 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002174 // special-case 2 points (common for drawing a single line)
2175 if (2 == count) {
2176 r.set(pts[0], pts[1]);
2177 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002178 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002179 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002180 if (!r.isFinite()) {
2181 return;
2182 }
Mike Reed822128b2017-02-28 16:41:03 -05002183 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002184 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2185 return;
2186 }
2187 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002188 }
reed@google.coma584aed2012-05-16 14:06:02 +00002189
halcanary96fcdcc2015-08-27 07:41:13 -07002190 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191
Mike Reed38992392019-07-30 10:48:15 -04002192 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002193
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002195 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196 }
reed@google.com4b226022011-01-11 18:32:13 +00002197
Mike Reed38992392019-07-30 10:48:15 -04002198 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199}
2200
reed4a167172016-08-18 17:15:25 -07002201static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002202 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002203}
2204
reed41af9662015-01-05 07:49:08 -08002205void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002206 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002208 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002209 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002210 return;
2211 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002212 }
reed@google.com4b226022011-01-11 18:32:13 +00002213
reed4a167172016-08-18 17:15:25 -07002214 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002215 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216
reed4a167172016-08-18 17:15:25 -07002217 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002218 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002219 }
2220
Mike Reed38992392019-07-30 10:48:15 -04002221 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002222 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002223 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002224 SkDrawIter iter(this);
2225 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002226 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002227 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002228 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002229}
2230
msarett44df6512016-08-25 13:54:30 -07002231void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002232 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002233 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002234 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002235 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2236 return;
2237 }
msarett44df6512016-08-25 13:54:30 -07002238 }
2239
Mike Reed38992392019-07-30 10:48:15 -04002240 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002241
2242 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002243 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002244 }
2245
Mike Reed38992392019-07-30 10:48:15 -04002246 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002247}
2248
Mike Reedd5674082019-04-19 15:00:47 -04002249void SkCanvas::onDrawBehind(const SkPaint& paint) {
2250 SkIRect bounds;
2251 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2252 for (;;) {
2253 const MCRec* rec = (const MCRec*)iter.prev();
2254 if (!rec) {
2255 return; // no backimages, so nothing to draw
2256 }
2257 if (rec->fBackImage) {
2258 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2259 rec->fBackImage->fImage->width(),
2260 rec->fBackImage->fImage->height());
2261 break;
2262 }
2263 }
2264
Mike Reed38992392019-07-30 10:48:15 -04002265 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002266
2267 while (iter.next()) {
2268 SkBaseDevice* dev = iter.fDevice;
2269
Mike Reedd5674082019-04-19 15:00:47 -04002270 dev->save();
2271 // We use clipRegion because it is already defined to operate in dev-space
2272 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2273 // but we don't want that, so we undo that before calling in.
Michael Ludwig915b7792019-10-22 17:40:41 +00002274 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002275 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002276 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002277 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002278 }
2279
Mike Reed38992392019-07-30 10:48:15 -04002280 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002281}
2282
reed41af9662015-01-05 07:49:08 -08002283void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002284 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002285 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002286 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002287 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002288 return;
2289 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002290 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002291
Mike Reed38992392019-07-30 10:48:15 -04002292 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002293
2294 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002295 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002296 }
2297
Mike Reed38992392019-07-30 10:48:15 -04002298 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002299}
2300
bsalomonac3aa242016-08-19 11:25:19 -07002301void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2302 SkScalar sweepAngle, bool useCenter,
2303 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002304 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002305 if (paint.canComputeFastBounds()) {
2306 SkRect storage;
2307 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002308 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002309 return;
2310 }
bsalomonac3aa242016-08-19 11:25:19 -07002311 }
2312
Mike Reed38992392019-07-30 10:48:15 -04002313 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002314
2315 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002316 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002317 }
2318
Mike Reed38992392019-07-30 10:48:15 -04002319 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002320}
2321
reed41af9662015-01-05 07:49:08 -08002322void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002323 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002324 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002325 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2326 return;
2327 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002328 }
2329
2330 if (rrect.isRect()) {
2331 // call the non-virtual version
2332 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002333 return;
2334 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002335 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002336 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2337 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002338 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002339
Mike Reed38992392019-07-30 10:48:15 -04002340 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002341
2342 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002343 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002344 }
2345
Mike Reed38992392019-07-30 10:48:15 -04002346 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002347}
2348
Mike Reed822128b2017-02-28 16:41:03 -05002349void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002350 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002351 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002352 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2353 return;
2354 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002355 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002356
Mike Reed38992392019-07-30 10:48:15 -04002357 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002358
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002359 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002360 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002361 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002362
Mike Reed38992392019-07-30 10:48:15 -04002363 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002364}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002365
reed41af9662015-01-05 07:49:08 -08002366void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002367 if (!path.isFinite()) {
2368 return;
2369 }
2370
Mike Reed822128b2017-02-28 16:41:03 -05002371 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002372 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002373 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002374 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2375 return;
2376 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002378
Mike Reed822128b2017-02-28 16:41:03 -05002379 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002380 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002381 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002382 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002383 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002384 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385
Mike Reed38992392019-07-30 10:48:15 -04002386 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387
2388 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002389 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 }
2391
Mike Reed38992392019-07-30 10:48:15 -04002392 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002393}
2394
reed262a71b2015-12-05 13:07:27 -08002395bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002396 if (!paint.getImageFilter()) {
2397 return false;
2398 }
2399
2400 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002401 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002402 return false;
2403 }
2404
2405 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2406 // Once we can filter and the filter will return a result larger than itself, we should be
2407 // able to remove this constraint.
2408 // skbug.com/4526
2409 //
2410 SkPoint pt;
2411 ctm.mapXY(x, y, &pt);
2412 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2413 return ir.contains(fMCRec->fRasterClip.getBounds());
2414}
2415
Mike Reedf441cfc2018-04-11 14:50:16 -04002416// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2417// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2418// null.
2419static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2420 if (paintParam) {
2421 *real = *paintParam;
2422 real->setStyle(SkPaint::kFill_Style);
2423 real->setPathEffect(nullptr);
2424 paintParam = real;
2425 }
2426 return paintParam;
2427}
2428
reeda85d4d02015-05-06 12:56:48 -07002429void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002430 SkPaint realPaint;
2431 paint = init_image_paint(&realPaint, paint);
2432
reeda85d4d02015-05-06 12:56:48 -07002433 SkRect bounds = SkRect::MakeXYWH(x, y,
2434 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002435 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002436 SkRect tmp = bounds;
2437 if (paint) {
2438 paint->computeFastBounds(tmp, &tmp);
2439 }
2440 if (this->quickReject(tmp)) {
2441 return;
2442 }
reeda85d4d02015-05-06 12:56:48 -07002443 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002444 // At this point we need a real paint object. If the caller passed null, then we should
2445 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2446 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2447 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002448
reeda2217ef2016-07-20 06:04:34 -07002449 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002450 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2451 *paint);
2452 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002453 special = this->getDevice()->makeSpecial(image);
2454 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002455 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002456 }
2457 }
2458
Mike Reed38992392019-07-30 10:48:15 -04002459 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002460
reeda85d4d02015-05-06 12:56:48 -07002461 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002462 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002463 if (special) {
2464 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002465 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002466 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002467 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002468 SkScalarRoundToInt(pt.fY), pnt,
2469 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002470 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002471 iter.fDevice->drawImageRect(
2472 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2473 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002474 }
reeda85d4d02015-05-06 12:56:48 -07002475 }
halcanary9d524f22016-03-29 09:03:52 -07002476
Mike Reed38992392019-07-30 10:48:15 -04002477 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002478}
2479
reed41af9662015-01-05 07:49:08 -08002480void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002481 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002482 SkPaint realPaint;
2483 paint = init_image_paint(&realPaint, paint);
2484
halcanary96fcdcc2015-08-27 07:41:13 -07002485 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002486 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002487 if (paint) {
2488 paint->computeFastBounds(dst, &storage);
2489 }
2490 if (this->quickReject(storage)) {
2491 return;
2492 }
reeda85d4d02015-05-06 12:56:48 -07002493 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002494 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002495
Mike Reed38992392019-07-30 10:48:15 -04002496 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002497
reeda85d4d02015-05-06 12:56:48 -07002498 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002499 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002500 }
halcanary9d524f22016-03-29 09:03:52 -07002501
Mike Reed38992392019-07-30 10:48:15 -04002502 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002503}
2504
reed41af9662015-01-05 07:49:08 -08002505void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002506 SkDEBUGCODE(bitmap.validate();)
2507
reed33366972015-10-08 09:22:02 -07002508 if (bitmap.drawsNothing()) {
2509 return;
2510 }
2511
Mike Reedf441cfc2018-04-11 14:50:16 -04002512 SkPaint realPaint;
2513 init_image_paint(&realPaint, paint);
2514 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002515
Mike Reed822128b2017-02-28 16:41:03 -05002516 SkRect bounds;
2517 bitmap.getBounds(&bounds);
2518 bounds.offset(x, y);
2519 bool canFastBounds = paint->canComputeFastBounds();
2520 if (canFastBounds) {
2521 SkRect storage;
2522 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002523 return;
2524 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002525 }
reed@google.com4b226022011-01-11 18:32:13 +00002526
reeda2217ef2016-07-20 06:04:34 -07002527 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002528 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2529 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002530 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002531 special = this->getDevice()->makeSpecial(bitmap);
2532 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002533 drawAsSprite = false;
2534 }
2535 }
2536
Mike Reed38992392019-07-30 10:48:15 -04002537 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002538
2539 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002540 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002541 if (special) {
reed262a71b2015-12-05 13:07:27 -08002542 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002543 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002544 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002545 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002546 SkScalarRoundToInt(pt.fY), pnt,
2547 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002548 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002549 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2550 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2551 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002552 }
reed33366972015-10-08 09:22:02 -07002553 }
msarettfbfa2582016-08-12 08:29:08 -07002554
Mike Reed38992392019-07-30 10:48:15 -04002555 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002556}
2557
reed@google.com9987ec32011-09-07 11:57:52 +00002558// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002559void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002560 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002561 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002562 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002563 return;
2564 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002565
halcanary96fcdcc2015-08-27 07:41:13 -07002566 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002567 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002568 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2569 return;
2570 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571 }
reed@google.com3d608122011-11-21 15:16:16 +00002572
reed@google.com33535f32012-09-25 15:37:50 +00002573 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002574 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002575 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002576 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002577
Mike Reed38992392019-07-30 10:48:15 -04002578 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002579
reed@google.com33535f32012-09-25 15:37:50 +00002580 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002581 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002582 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002583
Mike Reed38992392019-07-30 10:48:15 -04002584 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002585}
2586
reed41af9662015-01-05 07:49:08 -08002587void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002588 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002589 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002590 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002591}
2592
reed4c21dc52015-06-25 12:32:03 -07002593void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2594 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002595 SkPaint realPaint;
2596 paint = init_image_paint(&realPaint, paint);
2597
halcanary96fcdcc2015-08-27 07:41:13 -07002598 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002599 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002600 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2601 return;
2602 }
reed@google.com3d608122011-11-21 15:16:16 +00002603 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002604 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002605
Mike Reed38992392019-07-30 10:48:15 -04002606 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002607
reed4c21dc52015-06-25 12:32:03 -07002608 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002609 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002610 }
halcanary9d524f22016-03-29 09:03:52 -07002611
Mike Reed38992392019-07-30 10:48:15 -04002612 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002613}
2614
reed41af9662015-01-05 07:49:08 -08002615void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2616 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002617 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002618 SkPaint realPaint;
2619 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002620
halcanary96fcdcc2015-08-27 07:41:13 -07002621 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002622 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002623 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2624 return;
2625 }
reed4c21dc52015-06-25 12:32:03 -07002626 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002627 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002628
Mike Reed38992392019-07-30 10:48:15 -04002629 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002630
reed4c21dc52015-06-25 12:32:03 -07002631 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002632 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002633 }
halcanary9d524f22016-03-29 09:03:52 -07002634
Mike Reed38992392019-07-30 10:48:15 -04002635 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002636}
2637
msarett16882062016-08-16 09:31:08 -07002638void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2639 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002640 SkPaint realPaint;
2641 paint = init_image_paint(&realPaint, paint);
2642
msarett16882062016-08-16 09:31:08 -07002643 if (nullptr == paint || paint->canComputeFastBounds()) {
2644 SkRect storage;
2645 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2646 return;
2647 }
2648 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002649 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002650
Mike Reed38992392019-07-30 10:48:15 -04002651 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002652
2653 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002654 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002655 }
2656
Mike Reed38992392019-07-30 10:48:15 -04002657 DRAW_END
msarett16882062016-08-16 09:31:08 -07002658}
2659
2660void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2661 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002662 SkPaint realPaint;
2663 paint = init_image_paint(&realPaint, paint);
2664
msarett16882062016-08-16 09:31:08 -07002665 if (nullptr == paint || paint->canComputeFastBounds()) {
2666 SkRect storage;
2667 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2668 return;
2669 }
2670 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002671 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002672
Mike Reed38992392019-07-30 10:48:15 -04002673 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002674
2675 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002676 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002677 }
2678
Mike Reed38992392019-07-30 10:48:15 -04002679 DRAW_END
msarett16882062016-08-16 09:31:08 -07002680}
2681
fmalita00d5c2c2014-08-21 08:53:26 -07002682void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2683 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002684 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002685 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002686 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002687 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002688 SkRect tmp;
2689 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2690 return;
2691 }
2692 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002693 }
2694
fmalita024f9962015-03-03 19:08:17 -08002695 // We cannot filter in the looper as we normally do, because the paint is
2696 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002697 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002698
fmalitaaa1b9122014-08-28 14:32:24 -07002699 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002700 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002701 }
2702
Mike Reed38992392019-07-30 10:48:15 -04002703 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002704}
2705
Mike Reed358fcad2018-11-23 15:27:51 -05002706// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002707void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002708 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2709 TRACE_EVENT0("skia", TRACE_FUNC);
2710 if (byteLength) {
2711 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002712 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002713 }
2714}
Mike Reed4f81bb72019-01-23 09:23:00 -05002715
fmalita00d5c2c2014-08-21 08:53:26 -07002716void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2717 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002718 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002719 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002720 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002721 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002722}
reed@google.come0d9ce82014-04-23 04:00:17 +00002723
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002724void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002725 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002726 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002727
2728 while (iter.next()) {
2729 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002730 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002731 }
2732
Mike Reed38992392019-07-30 10:48:15 -04002733 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002734}
2735
dandovb3c9d1c2014-08-12 08:34:29 -07002736void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002737 const SkPoint texCoords[4], SkBlendMode bmode,
2738 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002739 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002740 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002741 return;
2742 }
mtklein6cfa73a2014-08-13 13:33:49 -07002743
Mike Reedfaba3712016-11-03 14:45:31 -04002744 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002745}
2746
2747void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002748 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002749 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002750 // Since a patch is always within the convex hull of the control points, we discard it when its
2751 // bounding rectangle is completely outside the current clip.
2752 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002753 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002754 if (this->quickReject(bounds)) {
2755 return;
2756 }
mtklein6cfa73a2014-08-13 13:33:49 -07002757
Mike Reed38992392019-07-30 10:48:15 -04002758 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002759
dandovecfff212014-08-04 10:02:00 -07002760 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002761 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002762 }
mtklein6cfa73a2014-08-13 13:33:49 -07002763
Mike Reed38992392019-07-30 10:48:15 -04002764 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002765}
2766
reeda8db7282015-07-07 10:22:31 -07002767void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002768#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002769 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002770#endif
reede3b38ce2016-01-08 09:18:44 -08002771 RETURN_ON_NULL(dr);
2772 if (x || y) {
2773 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2774 this->onDrawDrawable(dr, &matrix);
2775 } else {
2776 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002777 }
2778}
2779
reeda8db7282015-07-07 10:22:31 -07002780void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002781#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002782 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002783#endif
reede3b38ce2016-01-08 09:18:44 -08002784 RETURN_ON_NULL(dr);
2785 if (matrix && matrix->isIdentity()) {
2786 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002787 }
reede3b38ce2016-01-08 09:18:44 -08002788 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002789}
2790
2791void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002792 // drawable bounds are no longer reliable (e.g. android displaylist)
2793 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002794 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002795}
2796
reed71c3c762015-06-24 10:29:17 -07002797void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002798 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002799 const SkRect* cull, const SkPaint* paint) {
2800 if (cull && this->quickReject(*cull)) {
2801 return;
2802 }
2803
2804 SkPaint pnt;
2805 if (paint) {
2806 pnt = *paint;
2807 }
halcanary9d524f22016-03-29 09:03:52 -07002808
Mike Reed38992392019-07-30 10:48:15 -04002809 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002810 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002811 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002812 }
Mike Reed38992392019-07-30 10:48:15 -04002813 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002814}
2815
reedf70b5312016-03-04 16:36:20 -08002816void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2817 SkASSERT(key);
2818
2819 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002820 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002821 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002822 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002823 }
Mike Reed38992392019-07-30 10:48:15 -04002824 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002825}
2826
Michael Ludwiga595f862019-08-27 15:25:49 -04002827void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2828 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002829 SkASSERT(r.isSorted());
2830
2831 // If this used a paint, it would be a filled color with blend mode, which does not
2832 // need to use an autodraw loop, so use SkDrawIter directly.
2833 if (this->quickReject(r)) {
2834 return;
2835 }
2836
Michael Ludwiga4b44882019-08-28 14:34:58 -04002837 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002838 SkDrawIter iter(this);
2839 while(iter.next()) {
2840 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2841 }
2842}
2843
2844void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2845 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2846 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002847 if (count <= 0) {
2848 // Nothing to draw
2849 return;
2850 }
2851
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002852 SkPaint realPaint;
2853 init_image_paint(&realPaint, paint);
2854
Michael Ludwiga4b44882019-08-28 14:34:58 -04002855 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2856 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2857 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2858 // or we need it for the autolooper (since it greatly improves image filter perf).
2859 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2860 bool setBoundsValid = count == 1 || needsAutoLooper;
2861 SkRect setBounds = imageSet[0].fDstRect;
2862 if (imageSet[0].fMatrixIndex >= 0) {
2863 // Account for the per-entry transform that is applied prior to the CTM when drawing
2864 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002865 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002866 if (needsAutoLooper) {
2867 for (int i = 1; i < count; ++i) {
2868 SkRect entryBounds = imageSet[i].fDstRect;
2869 if (imageSet[i].fMatrixIndex >= 0) {
2870 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2871 }
2872 setBounds.joinPossiblyEmptyRect(entryBounds);
2873 }
2874 }
2875
2876 // If we happen to have the draw bounds, though, might as well check quickReject().
2877 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2878 SkRect tmp;
2879 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2880 return;
2881 }
2882 }
2883
2884 if (needsAutoLooper) {
2885 SkASSERT(setBoundsValid);
2886 DRAW_BEGIN(realPaint, &setBounds)
2887 while (iter.next()) {
2888 iter.fDevice->drawEdgeAAImageSet(
2889 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2890 }
2891 DRAW_END
2892 } else {
2893 this->predrawNotify();
2894 SkDrawIter iter(this);
2895 while(iter.next()) {
2896 iter.fDevice->drawEdgeAAImageSet(
2897 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2898 }
2899 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002900}
2901
reed@android.com8a1c16f2008-12-17 15:59:43 +00002902//////////////////////////////////////////////////////////////////////////////
2903// These methods are NOT virtual, and therefore must call back into virtual
2904// methods, rather than actually drawing themselves.
2905//////////////////////////////////////////////////////////////////////////////
2906
reed374772b2016-10-05 17:33:02 -07002907void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002908 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002909 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002910 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002911 this->drawPaint(paint);
2912}
2913
2914void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002915 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002916 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2917}
2918
Mike Reed3661bc92017-02-22 13:21:42 -05002919void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002921 pts[0].set(x0, y0);
2922 pts[1].set(x1, y1);
2923 this->drawPoints(kLines_PointMode, 2, pts, paint);
2924}
2925
Mike Reed3661bc92017-02-22 13:21:42 -05002926void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927 if (radius < 0) {
2928 radius = 0;
2929 }
2930
2931 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002932 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002933 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934}
2935
2936void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2937 const SkPaint& paint) {
2938 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002939 SkRRect rrect;
2940 rrect.setRectXY(r, rx, ry);
2941 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002942 } else {
2943 this->drawRect(r, paint);
2944 }
2945}
2946
reed@android.com8a1c16f2008-12-17 15:59:43 +00002947void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2948 SkScalar sweepAngle, bool useCenter,
2949 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002950 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002951 if (oval.isEmpty() || !sweepAngle) {
2952 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953 }
bsalomon21af9ca2016-08-25 12:29:23 -07002954 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955}
2956
reed@android.comf76bacf2009-05-13 14:00:33 +00002957///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002958#ifdef SK_DISABLE_SKPICTURE
2959void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002960
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002961
2962void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2963 const SkPaint* paint) {}
2964#else
Mike Klein88d90712018-01-27 17:30:04 +00002965/**
2966 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2967 * against the playback cost of recursing into the subpicture to get at its actual ops.
2968 *
2969 * For now we pick a conservatively small value, though measurement (and other heuristics like
2970 * the type of ops contained) may justify changing this value.
2971 */
2972#define kMaxPictureOpsToUnrollInsteadOfRef 1
2973
reedd5fa1a42014-08-09 11:08:05 -07002974void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002975 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002976 RETURN_ON_NULL(picture);
2977
reede3b38ce2016-01-08 09:18:44 -08002978 if (matrix && matrix->isIdentity()) {
2979 matrix = nullptr;
2980 }
Mike Klein88d90712018-01-27 17:30:04 +00002981 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2982 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2983 picture->playback(this);
2984 } else {
2985 this->onDrawPicture(picture, matrix, paint);
2986 }
reedd5fa1a42014-08-09 11:08:05 -07002987}
robertphillips9b14f262014-06-04 05:40:44 -07002988
reedd5fa1a42014-08-09 11:08:05 -07002989void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2990 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002991 if (!paint || paint->canComputeFastBounds()) {
2992 SkRect bounds = picture->cullRect();
2993 if (paint) {
2994 paint->computeFastBounds(bounds, &bounds);
2995 }
2996 if (matrix) {
2997 matrix->mapRect(&bounds);
2998 }
2999 if (this->quickReject(bounds)) {
3000 return;
3001 }
3002 }
3003
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003004 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003005 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003006}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003007#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003008
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009///////////////////////////////////////////////////////////////////////////////
3010///////////////////////////////////////////////////////////////////////////////
3011
reed3aafe112016-08-18 12:45:34 -07003012SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003013 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003014
3015 SkASSERT(canvas);
3016
reed3aafe112016-08-18 12:45:34 -07003017 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003018 fDone = !fImpl->next();
3019}
3020
3021SkCanvas::LayerIter::~LayerIter() {
3022 fImpl->~SkDrawIter();
3023}
3024
3025void SkCanvas::LayerIter::next() {
3026 fDone = !fImpl->next();
3027}
3028
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003029SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003030 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003031}
3032
3033const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003034 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003035}
3036
3037const SkPaint& SkCanvas::LayerIter::paint() const {
3038 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003039 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003040 paint = &fDefaultPaint;
3041 }
3042 return *paint;
3043}
3044
Mike Reedca37f322018-03-08 13:22:16 -05003045SkIRect SkCanvas::LayerIter::clipBounds() const {
3046 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003047}
3048
Michael Ludwig915b7792019-10-22 17:40:41 +00003049int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3050int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003051
3052///////////////////////////////////////////////////////////////////////////////
3053
Brian Osmane8a98632019-04-10 10:26:10 -04003054SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3055SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3056SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3057SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3058
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003059SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3060 const SkRect& dstRect, int matrixIndex, float alpha,
3061 unsigned aaFlags, bool hasClip)
3062 : fImage(std::move(image))
3063 , fSrcRect(srcRect)
3064 , fDstRect(dstRect)
3065 , fMatrixIndex(matrixIndex)
3066 , fAlpha(alpha)
3067 , fAAFlags(aaFlags)
3068 , fHasClip(hasClip) {}
3069
3070SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3071 const SkRect& dstRect, float alpha, unsigned aaFlags)
3072 : fImage(std::move(image))
3073 , fSrcRect(srcRect)
3074 , fDstRect(dstRect)
3075 , fAlpha(alpha)
3076 , fAAFlags(aaFlags) {}
3077
3078///////////////////////////////////////////////////////////////////////////////
3079
Mike Reed5df49342016-11-12 08:06:55 -06003080std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003081 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003082 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003083 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003084 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003085
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003086 SkBitmap bitmap;
3087 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003088 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003089 }
Mike Reed12f77342017-11-08 11:19:52 -05003090
3091 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003092 std::make_unique<SkCanvas>(bitmap, *props) :
3093 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003094}
reedd5fa1a42014-08-09 11:08:05 -07003095
3096///////////////////////////////////////////////////////////////////////////////
3097
Florin Malitaee424ac2016-12-01 12:47:59 -05003098SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003099 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003100
Florin Malita439ace92016-12-02 12:05:41 -05003101SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003102 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003103
Herb Derbyefe39bc2018-05-01 17:06:20 -04003104SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003105 : INHERITED(device) {}
3106
Florin Malitaee424ac2016-12-01 12:47:59 -05003107SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3108 (void)this->INHERITED::getSaveLayerStrategy(rec);
3109 return kNoLayer_SaveLayerStrategy;
3110}
3111
Mike Reed148b7fd2018-12-18 17:38:18 -05003112bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3113 return false;
3114}
3115
Florin Malitaee424ac2016-12-01 12:47:59 -05003116///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003117
reed73603f32016-09-20 08:42:38 -07003118static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3119static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3120static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3121static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3122static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3123static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003124
3125///////////////////////////////////////////////////////////////////////////////////////////////////
3126
3127SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3128 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003129 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003130 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwig915b7792019-10-22 17:40:41 +00003131 SkIPoint origin = dev->getOrigin();
3132 SkMatrix ctm = this->getTotalMatrix();
3133 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3134
3135 SkIRect clip = fMCRec->fRasterClip.getBounds();
3136 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003137 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003138 clip.setEmpty();
3139 }
3140
Michael Ludwig915b7792019-10-22 17:40:41 +00003141 fAllocator->updateHandle(handle, ctm, clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003142 return handle;
3143 }
3144 return nullptr;
3145}
3146
3147static bool install(SkBitmap* bm, const SkImageInfo& info,
3148 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003149 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003150}
3151
3152SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3153 SkBitmap* bm) {
3154 SkRasterHandleAllocator::Rec rec;
3155 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3156 return nullptr;
3157 }
3158 return rec.fHandle;
3159}
3160
3161std::unique_ptr<SkCanvas>
3162SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3163 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003164 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003165 return nullptr;
3166 }
3167
3168 SkBitmap bm;
3169 Handle hndl;
3170
3171 if (rec) {
3172 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3173 } else {
3174 hndl = alloc->allocBitmap(info, &bm);
3175 }
3176 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3177}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003178
3179///////////////////////////////////////////////////////////////////////////////////////////////////