blob: a36f36ef8aec4e2c93a76f5ab77d694784309741 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/private/SkNx.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040023#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050025#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050035#include "src/core/SkMatrixUtils.h"
36#include "src/core/SkPaintPriv.h"
37#include "src/core/SkRasterClip.h"
38#include "src/core/SkSpecialImage.h"
39#include "src/core/SkStrikeCache.h"
40#include "src/core/SkTLazy.h"
41#include "src/core/SkTextFormatParams.h"
42#include "src/core/SkTraceEvent.h"
43#include "src/image/SkImage_Base.h"
44#include "src/image/SkSurface_Base.h"
45#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040046
bungemand3ebb482015-08-05 13:57:49 -070047#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000048
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000049#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050050#include "include/gpu/GrContext.h"
51#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#endif
53
reede3b38ce2016-01-08 09:18:44 -080054#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050055#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080056
Mike Klein1bb7e232019-12-10 08:58:52 -060057// This is a test: static_assert with no message is a c++17 feature,
58// and std::max() is constexpr only since the c++14 stdlib.
59static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000060
Mike Reed139e5e02017-03-08 11:29:33 -050061///////////////////////////////////////////////////////////////////////////////////////////////////
62
reedc83a2972015-07-16 07:40:45 -070063/*
64 * Return true if the drawing this rect would hit every pixels in the canvas.
65 *
66 * Returns false if
67 * - rect does not contain the canvas' bounds
68 * - paint is not fill
69 * - paint would blur or otherwise change the coverage of the rect
70 */
71bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
72 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070073 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
74 (int)kNone_ShaderOverrideOpacity,
75 "need_matching_enums0");
76 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
77 (int)kOpaque_ShaderOverrideOpacity,
78 "need_matching_enums1");
79 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
80 (int)kNotOpaque_ShaderOverrideOpacity,
81 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070082
83 const SkISize size = this->getBaseLayerSize();
84 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050085
86 // if we're clipped at all, we can't overwrite the entire surface
87 {
88 SkBaseDevice* base = this->getDevice();
89 SkBaseDevice* top = this->getTopDevice();
90 if (base != top) {
91 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
92 }
93 if (!base->clipIsWideOpen()) {
94 return false;
95 }
reedc83a2972015-07-16 07:40:45 -070096 }
97
98 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070099 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700100 return false; // conservative
101 }
halcanaryc5769b22016-08-10 07:13:21 -0700102
103 SkRect devRect;
104 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
105 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700106 return false;
107 }
108 }
109
110 if (paint) {
111 SkPaint::Style paintStyle = paint->getStyle();
112 if (!(paintStyle == SkPaint::kFill_Style ||
113 paintStyle == SkPaint::kStrokeAndFill_Style)) {
114 return false;
115 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400116 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700117 return false; // conservative
118 }
119 }
120 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
121}
122
123///////////////////////////////////////////////////////////////////////////////////////////////////
124
reed@google.comda17f752012-08-16 18:27:05 +0000125// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126//#define SK_TRACE_SAVERESTORE
127
128#ifdef SK_TRACE_SAVERESTORE
129 static int gLayerCounter;
130 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
131 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
132
133 static int gRecCounter;
134 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
135 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
136
137 static int gCanvasCounter;
138 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
139 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
140#else
141 #define inc_layer()
142 #define dec_layer()
143 #define inc_rec()
144 #define dec_rec()
145 #define inc_canvas()
146 #define dec_canvas()
147#endif
148
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000149typedef SkTLazy<SkPaint> SkLazyPaint;
150
reedc83a2972015-07-16 07:40:45 -0700151void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000152 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700153 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
154 ? SkSurface::kDiscard_ContentChangeMode
155 : SkSurface::kRetain_ContentChangeMode);
156 }
157}
158
159void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
160 ShaderOverrideOpacity overrideOpacity) {
161 if (fSurfaceBase) {
162 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
163 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
164 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
165 // and therefore we don't care which mode we're in.
166 //
167 if (fSurfaceBase->outstandingImageSnapshot()) {
168 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
169 mode = SkSurface::kDiscard_ContentChangeMode;
170 }
171 }
172 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000173 }
174}
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000178/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 The clip/matrix/proc are fields that reflect the top of the save/restore
180 stack. Whenever the canvas changes, it marks a dirty flag, and then before
181 these are used (assuming we're not on a layer) we rebuild these cache
182 values: they reflect the top of the save stack, but translated and clipped
183 by the device's XY offset and bitmap-bounds.
184*/
185struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400186 DeviceCM* fNext;
187 sk_sp<SkBaseDevice> fDevice;
188 SkRasterClip fClip;
189 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
190 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400191 sk_sp<SkImage> fClipImage;
192 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
Florin Malita53f77bd2017-04-28 13:48:37 -0400194 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000195 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700196 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400197 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500198 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700199 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000200 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400201 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400202 {}
reed@google.com4b226022011-01-11 18:32:13 +0000203
mtkleinfeaadee2015-04-08 11:25:48 -0700204 void reset(const SkIRect& bounds) {
205 SkASSERT(!fPaint);
206 SkASSERT(!fNext);
207 SkASSERT(fDevice);
208 fClip.setRect(bounds);
209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210};
211
Mike Reed148b7fd2018-12-18 17:38:18 -0500212namespace {
213// Encapsulate state needed to restore from saveBehind()
214struct BackImage {
215 sk_sp<SkSpecialImage> fImage;
216 SkIPoint fLoc;
217};
218}
219
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220/* This is the record we keep for each save/restore level in the stack.
221 Since a level optionally copies the matrix and/or stack, we have pointers
222 for these fields. If the value is copied for this level, the copy is
223 stored in the ...Storage field, and the pointer points to that. If the
224 value is not copied for this level, we ignore ...Storage, and just point
225 at the corresponding value in the previous level in the stack.
226*/
227class SkCanvas::MCRec {
228public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500229 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 /* If there are any layers in the stack, this points to the top-most
231 one that is at or below this level in the stack (so we know what
232 bitmap/device to draw into from this level. This value is NOT
233 reference counted, since the real owner is either our fLayer field,
234 or a previous one in a lower level.)
235 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500236 DeviceCM* fTopLayer;
237 std::unique_ptr<BackImage> fBackImage;
238 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500239 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500240 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241
Mike Reeda1361362017-03-07 09:37:29 -0500242 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700243 fLayer = nullptr;
244 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800245 fMatrix.reset();
246 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700247
reedd9544982014-09-09 18:46:22 -0700248 // don't bother initializing fNext
249 inc_rec();
250 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400251 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700252 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700253 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800254 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 // don't bother initializing fNext
257 inc_rec();
258 }
259 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700260 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 dec_rec();
262 }
mtkleinfeaadee2015-04-08 11:25:48 -0700263
264 void reset(const SkIRect& bounds) {
265 SkASSERT(fLayer);
266 SkASSERT(fDeferredSaveCount == 0);
267
268 fMatrix.reset();
269 fRasterClip.setRect(bounds);
270 fLayer->reset(bounds);
271 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
Mike Reeda1361362017-03-07 09:37:29 -0500274class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275public:
Mike Reeda1361362017-03-07 09:37:29 -0500276 SkDrawIter(SkCanvas* canvas)
277 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
278 {}
reed@google.com4b226022011-01-11 18:32:13 +0000279
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000281 const DeviceCM* rec = fCurrLayer;
282 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400283 fDevice = rec->fDevice.get();
284 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700286 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 return true;
288 }
289 return false;
290 }
reed@google.com4b226022011-01-11 18:32:13 +0000291
Michael Ludwig915b7792019-10-22 17:40:41 +0000292 int getX() const { return fDevice->getOrigin().x(); }
293 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000295
Mike Reed99330ba2017-02-22 11:01:08 -0500296 SkBaseDevice* fDevice;
297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 const DeviceCM* fCurrLayer;
300 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301};
302
Florin Malita713b8ef2017-04-28 10:57:24 -0400303#define FOR_EACH_TOP_DEVICE( code ) \
304 do { \
305 DeviceCM* layer = fMCRec->fTopLayer; \
306 while (layer) { \
307 SkBaseDevice* device = layer->fDevice.get(); \
308 if (device) { \
309 code; \
310 } \
311 layer = layer->fNext; \
312 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500313 } while (0)
314
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315/////////////////////////////////////////////////////////////////////////////
316
reeddbc3cef2015-04-29 12:18:57 -0700317/**
318 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700319 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700320 */
reedd053ce92016-03-22 10:17:23 -0700321static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700322 SkImageFilter* imgf = paint.getImageFilter();
323 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700324 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700325 }
326
reedd053ce92016-03-22 10:17:23 -0700327 SkColorFilter* imgCFPtr;
328 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700329 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700330 }
reedd053ce92016-03-22 10:17:23 -0700331 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700332
333 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700334 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700335 // there is no existing paint colorfilter, so we can just return the imagefilter's
336 return imgCF;
337 }
338
339 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
340 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500341 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700342}
343
senorblanco87e066e2015-10-28 11:23:36 -0700344/**
345 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
346 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
347 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
348 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
349 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
350 * conservative "effective" bounds based on the settings in the paint... with one exception. This
351 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
352 * deliberately ignored.
353 */
354static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
355 const SkRect& rawBounds,
356 SkRect* storage) {
357 SkPaint tmpUnfiltered(paint);
358 tmpUnfiltered.setImageFilter(nullptr);
359 if (tmpUnfiltered.canComputeFastBounds()) {
360 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
361 } else {
362 return rawBounds;
363 }
364}
365
Mike Reed38992392019-07-30 10:48:15 -0400366class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367public:
senorblanco87e066e2015-10-28 11:23:36 -0700368 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
369 // paint. It's used to determine the size of the offscreen layer for filters.
370 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400371 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
372 bool skipLayerForImageFilter = false,
373 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400375 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700377 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378
Mike Reed38992392019-07-30 10:48:15 -0400379 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
380 SkASSERT(!fLazyPaint.isValid());
381 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700382 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700383 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700384 fPaint = paint;
385 }
386
387 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700388 /**
389 * We implement ImageFilters for a given draw by creating a layer, then applying the
390 * imagefilter to the pixels of that layer (its backing surface/image), and then
391 * we call restore() to xfer that layer to the main canvas.
392 *
393 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
394 * 2. Generate the src pixels:
395 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
396 * return (fPaint). We then draw the primitive (using srcover) into a cleared
397 * buffer/surface.
398 * 3. Restore the layer created in #1
399 * The imagefilter is passed the buffer/surface from the layer (now filled with the
400 * src pixels of the primitive). It returns a new "filtered" buffer, which we
401 * draw onto the previous layer using the xfermode from the original paint.
402 */
Mike Reed38992392019-07-30 10:48:15 -0400403
404 SkPaint restorePaint;
405 restorePaint.setImageFilter(fPaint->refImageFilter());
406 restorePaint.setBlendMode(fPaint->getBlendMode());
407
senorblanco87e066e2015-10-28 11:23:36 -0700408 SkRect storage;
409 if (rawBounds) {
410 // Make rawBounds include all paint outsets except for those due to image filters.
411 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
412 }
Mike Reed38992392019-07-30 10:48:15 -0400413 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700414 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700415 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000416
Mike Reed38992392019-07-30 10:48:15 -0400417 // Remove the restorePaint fields from our "working" paint
418 SkASSERT(!fLazyPaint.isValid());
419 SkPaint* paint = fLazyPaint.set(origPaint);
420 paint->setImageFilter(nullptr);
421 paint->setBlendMode(SkBlendMode::kSrcOver);
422 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423 }
424 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000425
Mike Reed38992392019-07-30 10:48:15 -0400426 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700427 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000428 fCanvas->internalRestore();
429 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000430 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000432
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433 const SkPaint& paint() const {
434 SkASSERT(fPaint);
435 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438private:
Mike Reed38992392019-07-30 10:48:15 -0400439 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000440 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400441 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000442 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700443 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444};
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446////////// macros to place around the internal draw calls //////////////////
447
Mike Reed38992392019-07-30 10:48:15 -0400448#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700449 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400450 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
451 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800452
453
Mike Reed38992392019-07-30 10:48:15 -0400454#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000455 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400456 AutoLayerForImageFilter draw(this, paint, true); \
457 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000458
Mike Reed38992392019-07-30 10:48:15 -0400459#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000460 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400461 AutoLayerForImageFilter draw(this, paint, false, bounds); \
462 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000463
Mike Reed38992392019-07-30 10:48:15 -0400464#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700465 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400466 AutoLayerForImageFilter draw(this, paint, false, bounds); \
467 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700468
Mike Reed38992392019-07-30 10:48:15 -0400469#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470
471////////////////////////////////////////////////////////////////////////////
472
msarettfbfa2582016-08-12 08:29:08 -0700473static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
474 if (bounds.isEmpty()) {
475 return SkRect::MakeEmpty();
476 }
477
478 // Expand bounds out by 1 in case we are anti-aliasing. We store the
479 // bounds as floats to enable a faster quick reject implementation.
480 SkRect dst;
481 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
482 return dst;
483}
484
mtkleinfeaadee2015-04-08 11:25:48 -0700485void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
486 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700487 fMCRec->reset(bounds);
488
489 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500490 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400491 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700492 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700493 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700494}
495
Hal Canary363a3f82018-10-04 11:04:48 -0400496void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800497 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500500 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500501 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700502 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503
reeda499f902015-05-01 09:34:31 -0700504 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
505 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400506 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509
halcanary96fcdcc2015-08-27 07:41:13 -0700510 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000511
reedf92c8662014-08-18 08:02:43 -0700512 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700513 // The root device and the canvas should always have the same pixel geometry
514 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800515 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700516 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700519 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400520
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500521 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
reed@google.comcde92112011-07-06 20:00:52 +0000524SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700526 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000527{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000528 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000529
Hal Canary363a3f82018-10-04 11:04:48 -0400530 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000531}
532
reed96a857e2015-01-25 10:33:58 -0800533SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800535 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000536{
537 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400538 this->init(sk_make_sp<SkNoPixelsDevice>(
Hal Canary363a3f82018-10-04 11:04:48 -0400539 SkIRect::MakeWH(SkTMax(width, 0), SkTMax(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700540}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000541
Hal Canary363a3f82018-10-04 11:04:48 -0400542SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700543 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700544 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700545{
546 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700547
Mike Reed566e53c2017-03-10 10:49:45 -0500548 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400549 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700550}
551
Herb Derbyefe39bc2018-05-01 17:06:20 -0400552SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000553 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700554 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000555{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700557
Hal Canary363a3f82018-10-04 11:04:48 -0400558 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700559}
560
reed4a8126e2014-09-22 07:29:03 -0700561SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700562 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700563 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700564{
565 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700566
Mike Reed910ca0f2018-04-25 13:04:05 -0400567 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400568 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700569}
reed29c857d2014-09-21 10:25:07 -0700570
Mike Reed356f7c22017-01-10 11:58:39 -0500571SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
572 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700573 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
574 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500575 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700576{
577 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700578
Mike Reed910ca0f2018-04-25 13:04:05 -0400579 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400580 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581}
582
Mike Reed356f7c22017-01-10 11:58:39 -0500583SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
584
Matt Sarett31f99ce2017-04-11 08:46:01 -0400585#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
586SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
587 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
588 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
589 , fAllocator(nullptr)
590{
591 inc_canvas();
592
593 SkBitmap tmp(bitmap);
594 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400595 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400596 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400597}
598#endif
599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600SkCanvas::~SkCanvas() {
601 // free up the contents of our deque
602 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 this->internalRestore(); // restore the last, since we're going away
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 dec_canvas();
607}
608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609///////////////////////////////////////////////////////////////////////////////
610
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000611void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700612 this->onFlush();
613}
614
615void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000616 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000617 if (device) {
618 device->flush();
619 }
620}
621
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500622SkSurface* SkCanvas::getSurface() const {
623 return fSurfaceBase;
624}
625
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000626SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000627 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000628 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
629}
630
senorblancoafc7cce2016-02-02 18:44:15 -0800631SkIRect SkCanvas::getTopLayerBounds() const {
632 SkBaseDevice* d = this->getTopDevice();
633 if (!d) {
634 return SkIRect::MakeEmpty();
635 }
Michael Ludwig915b7792019-10-22 17:40:41 +0000636 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
senorblancoafc7cce2016-02-02 18:44:15 -0800637}
638
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000639SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000641 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400643 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644}
645
Florin Malita0ed3b642017-01-13 16:56:38 +0000646SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400647 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000648}
649
Mike Reed353196f2017-07-21 11:01:18 -0400650bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000651 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400652 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000653}
654
Mike Reed353196f2017-07-21 11:01:18 -0400655bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
656 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400657}
658
659bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
660 SkPixmap pm;
661 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
662}
663
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000664bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400665 SkPixmap pm;
666 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700667 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000668 }
669 return false;
670}
671
Matt Sarett03dd6d52017-01-23 12:15:09 -0500672bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 SkBaseDevice* device = this->getDevice();
675 if (!device) {
676 return false;
677 }
678
Matt Sarett03dd6d52017-01-23 12:15:09 -0500679 // This check gives us an early out and prevents generation ID churn on the surface.
680 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
681 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400682 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500683 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685
Matt Sarett03dd6d52017-01-23 12:15:09 -0500686 // Tell our owning surface to bump its generation ID.
687 const bool completeOverwrite =
688 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700689 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700690
Matt Sarett03dd6d52017-01-23 12:15:09 -0500691 // This can still fail, most notably in the case of a invalid color type or alpha type
692 // conversion. We could pull those checks into this function and avoid the unnecessary
693 // generation ID bump. But then we would be performing those checks twice, since they
694 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400695 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000696}
reed@google.com51df9e32010-12-23 19:29:18 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698//////////////////////////////////////////////////////////////////////////////
699
reed2ff1fce2014-12-11 07:07:37 -0800700void SkCanvas::checkForDeferredSave() {
701 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800702 this->doSave();
703 }
704}
705
reedf0090cb2014-11-26 08:55:51 -0800706int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800707#ifdef SK_DEBUG
708 int count = 0;
709 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
710 for (;;) {
711 const MCRec* rec = (const MCRec*)iter.next();
712 if (!rec) {
713 break;
714 }
715 count += 1 + rec->fDeferredSaveCount;
716 }
717 SkASSERT(count == fSaveCount);
718#endif
719 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800720}
721
722int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800723 fSaveCount += 1;
724 fMCRec->fDeferredSaveCount += 1;
725 return this->getSaveCount() - 1; // return our prev value
726}
727
728void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800729 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700730
731 SkASSERT(fMCRec->fDeferredSaveCount > 0);
732 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800733 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800734}
735
736void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800737 if (fMCRec->fDeferredSaveCount > 0) {
738 SkASSERT(fSaveCount > 1);
739 fSaveCount -= 1;
740 fMCRec->fDeferredSaveCount -= 1;
741 } else {
742 // check for underflow
743 if (fMCStack.count() > 1) {
744 this->willRestore();
745 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700746 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800747 this->internalRestore();
748 this->didRestore();
749 }
reedf0090cb2014-11-26 08:55:51 -0800750 }
751}
752
753void SkCanvas::restoreToCount(int count) {
754 // sanity check
755 if (count < 1) {
756 count = 1;
757 }
mtkleinf0f14112014-12-12 08:46:25 -0800758
reedf0090cb2014-11-26 08:55:51 -0800759 int n = this->getSaveCount() - count;
760 for (int i = 0; i < n; ++i) {
761 this->restore();
762 }
763}
764
reed2ff1fce2014-12-11 07:07:37 -0800765void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700767 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000769
Mike Reedc42a1cd2017-02-14 14:25:14 -0500770 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771}
772
reed4960eee2015-12-18 07:09:18 -0800773bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400774 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775}
776
reed4960eee2015-12-18 07:09:18 -0800777bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700778 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400779 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
780 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
781 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
782 // filter does not require a decomposed CTM matrix, the filter space and device space are the
783 // same. When it has been decomposed, we want the original image filter node to process the
784 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
785 // longer the original filter, but has the remainder matrix baked into it, and passing in the
786 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
787 // to the original filter node (barring inflation from consecutive calls to mapRect). While
788 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
789 // passing getDeviceClipBounds() to 'imageFilter' is correct.
790 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
791 // be important to more accurately calculate the clip bounds in the layer space for the original
792 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500793 SkIRect clipBounds = this->getDeviceClipBounds();
794 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000795 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000796 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000797
reed96e657d2015-03-10 17:30:07 -0700798 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
799
Robert Phillips12078432018-05-17 11:17:39 -0400800 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
801 // If the image filter DAG affects transparent black then we will need to render
802 // out to the clip bounds
803 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000804 }
Robert Phillips12078432018-05-17 11:17:39 -0400805
806 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700807 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700809 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400810 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400812 inputSaveLayerBounds = clipBounds;
813 }
814
815 if (imageFilter) {
816 // expand the clip bounds by the image filter DAG to include extra content that might
817 // be required by the image filters.
818 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
819 SkImageFilter::kReverse_MapDirection,
820 &inputSaveLayerBounds);
821 }
822
823 SkIRect clippedSaveLayerBounds;
824 if (bounds) {
825 // For better or for worse, user bounds currently act as a hard clip on the layer's
826 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
827 clippedSaveLayerBounds = inputSaveLayerBounds;
828 } else {
829 // If there are no user bounds, we don't want to artificially restrict the resulting
830 // layer bounds, so allow the expanded clip bounds free reign.
831 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800833
834 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400835 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800836 if (BoundsAffectsClip(saveLayerFlags)) {
837 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
838 fMCRec->fRasterClip.setEmpty();
839 fDeviceClipBounds.setEmpty();
840 }
841 return false;
842 }
Robert Phillips12078432018-05-17 11:17:39 -0400843 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844
reed4960eee2015-12-18 07:09:18 -0800845 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700846 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400847 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
848 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000849 }
850
851 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400852 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000853 }
Robert Phillips12078432018-05-17 11:17:39 -0400854
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 return true;
856}
857
reed4960eee2015-12-18 07:09:18 -0800858int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
859 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000860}
861
Cary Clarke041e312018-03-06 13:00:52 -0500862int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700863 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400864 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
865 // no need for the layer (or any of the draws until the matching restore()
866 this->save();
867 this->clipRect({0,0,0,0});
868 } else {
869 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
870 fSaveCount += 1;
871 this->internalSaveLayer(rec, strategy);
872 }
reed4960eee2015-12-18 07:09:18 -0800873 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800874}
875
Mike Reed148b7fd2018-12-18 17:38:18 -0500876int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
877 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
878 // Assuming clips never expand, if the request bounds is outside of the current clip
879 // there is no need to copy/restore the area, so just devolve back to a regular save.
880 this->save();
881 } else {
882 bool doTheWork = this->onDoSaveBehind(bounds);
883 fSaveCount += 1;
884 this->internalSave();
885 if (doTheWork) {
886 this->internalSaveBehind(bounds);
887 }
888 }
889 return this->getSaveCount() - 1;
890}
891
reeda2217ef2016-07-20 06:04:34 -0700892void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500893 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500894 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400895 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
896 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400897 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig915b7792019-10-22 17:40:41 +0000898
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400899 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400900 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
901 // This means that we only have to copy a dst-sized block of pixels out of src and translate
902 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400903 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
904 dstOrigin.y() - src->getOrigin().y(),
905 dst->width(), dst->height());
906 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400907 return;
908 }
909
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400910 auto special = src->snapSpecial(snapBounds);
911 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400912 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
913 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400914 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
915 }
916 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400917 }
918
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400919 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
920 // by the backdrop filter.
921 SkMatrix toRoot, layerMatrix;
922 SkSize scale;
923 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
924 toRoot = SkMatrix::I();
925 layerMatrix = ctm;
926 } else if (ctm.decomposeScale(&scale, &toRoot)) {
927 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
928 } else {
929 // Perspective, for now, do no scaling of the layer itself.
930 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
931 // the matrix, e.g. based on the midpoint of the near/far planes?
932 toRoot = ctm;
933 layerMatrix = SkMatrix::I();
934 }
935
936 // We have to map the dst bounds from the root space into the layer space where filtering will
937 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
938 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
939 // is a safe, conservative estimate.
940 SkMatrix fromRoot;
941 if (!toRoot.invert(&fromRoot)) {
942 return;
943 }
944
945 // This represents what the backdrop filter needs to produce in the layer space, and is sized
946 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
947 SkIRect layerTargetBounds = fromRoot.mapRect(
948 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
949 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
950 // require some extra input pixels.
951 SkIRect layerInputBounds = filter->filterBounds(
952 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
953 &layerTargetBounds);
954
955 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400956 // be the conservative contents required to fill a layerInputBounds-sized surface with the
957 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400958 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
959 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
960 if (!backdropBounds.intersect(srcDevRect)) {
961 return;
962 }
963
964 auto special = src->snapSpecial(backdropBounds);
965 if (!special) {
966 return;
967 }
968
969 SkColorType colorType = src->imageInfo().colorType();
970 if (colorType == kUnknown_SkColorType) {
971 colorType = kRGBA_8888_SkColorType;
972 }
973 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400974
975 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400976 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400977 // Drawing the temporary and final filtered image requires a higher filter quality if the
978 // 'toRoot' transformation is not identity, in order to minimize the impact on already
979 // rendered edges/content.
980 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
981 p.setFilterQuality(kHigh_SkFilterQuality);
982
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400983 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
984 // and stored in a temporary surface, which is then used as the input to the actual filter.
985 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
986 if (!tmpSurface) {
987 return;
988 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400989
990 auto tmpCanvas = tmpSurface->getCanvas();
991 tmpCanvas->clear(SK_ColorTRANSPARENT);
992 // Reading in reverse, this takes the backdrop bounds from src device space into the root
993 // space, then maps from root space into the layer space, then maps it so the input layer's
994 // top left corner is (0, 0). This transformation automatically accounts for any cropping
995 // performed on backdropBounds.
996 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
997 tmpCanvas->concat(fromRoot);
998 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -0400999
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001000 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1001 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1002 special = tmpSurface->makeImageSnapshot();
1003 } else {
1004 // Since there is no extra transform that was done, update the input bounds to reflect
1005 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1006 // was equal to backdropBounds before it was made relative to the src device and cropped.
1007 // When we use the original snapped image directly, just map the update backdrop bounds
1008 // back into the shared layer space
1009 layerInputBounds = backdropBounds;
1010 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001011
1012 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1013 // draw will be 1-1 so there is no need to increase filter quality.
1014 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001015 }
1016
1017 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1018 // layer space. This has to further offset everything so that filter evaluation thinks the
1019 // source image's top left corner is (0, 0).
1020 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1021 // this can be simplified.
1022 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1023 SkMatrix filterCTM = layerMatrix;
1024 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1025 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1026
1027 SkIPoint offset;
1028 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001029 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001030 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1031 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1032 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1033 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001034 offset += layerInputBounds.topLeft();
1035
1036 // Manually setting the device's CTM requires accounting for the device's origin.
1037 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001038 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001039 // a global CTM instead of a device CTM.
1040 SkMatrix dstCTM = toRoot;
1041 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001042 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001043
1044 // And because devices don't have a special-image draw function that supports arbitrary
1045 // matrices, we are abusing the asImage() functionality here...
1046 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001047 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001048 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001049 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1051 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001052 }
robertphillips7354a4b2015-12-16 05:08:27 -08001053}
reed70ee31b2015-12-10 13:44:45 -08001054
Mike Kleine083f7c2018-02-07 12:54:27 -05001055static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001056 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001057 if (prev.bytesPerPixel() <= 4 &&
1058 prev.colorType() != kRGBA_8888_SkColorType &&
1059 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001060 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1061 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1062 ct = kN32_SkColorType;
1063 }
1064 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001065}
1066
reed4960eee2015-12-18 07:09:18 -08001067void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001068 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001069 const SkRect* bounds = rec.fBounds;
1070 const SkPaint* paint = rec.fPaint;
1071 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1072
Mike Reed5532c2a2019-02-23 12:00:32 -05001073 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1074 // regardless of any hint-rect from the caller. skbug.com/8783
1075 if (rec.fBackdrop) {
1076 bounds = nullptr;
1077 }
1078
reed8c30a812016-04-20 16:36:51 -07001079 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001080 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001081 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001082 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001083
reed8c30a812016-04-20 16:36:51 -07001084 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001085 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1086 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1087 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001088 *
1089 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001090 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1091 * if necessary.
1092 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1093 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001094 * 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 -04001095 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001096 * of the original imagefilter, and draw that (via drawSprite)
1097 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1098 *
1099 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1100 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1101 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001102 if (imageFilter) {
1103 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001104 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1105 &modifiedCTM);
1106 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001107 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001108 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001109 modifiedRec = fMCRec;
1110 this->internalSetMatrix(modifiedCTM);
1111 SkPaint* p = lazyP.set(*paint);
1112 p->setImageFilter(std::move(modifiedFilter));
1113 imageFilter = p->getImageFilter();
1114 paint = p;
1115 }
1116 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1117 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001118 }
reed8c30a812016-04-20 16:36:51 -07001119
junov@chromium.orga907ac32012-02-24 21:54:07 +00001120 // do this before we create the layer. We don't call the public save() since
1121 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001122 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001123
junov@chromium.orga907ac32012-02-24 21:54:07 +00001124 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001125 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001126 if (modifiedRec) {
1127 // In this case there will be no layer in which to stash the matrix so we need to
1128 // revert the prior MCRec to its earlier state.
1129 modifiedRec->fMatrix = stashedMatrix;
1130 }
reed2ff1fce2014-12-11 07:07:37 -08001131 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 }
1133
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001134 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1135 // the clipRectBounds() call above?
1136 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001137 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001138 }
1139
reed8dc0ccb2015-03-20 06:32:52 -07001140 SkPixelGeometry geo = fProps.pixelGeometry();
1141 if (paint) {
reed76033be2015-03-14 10:54:31 -07001142 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001143 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001144 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001145 }
1146 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147
robertphillips5139e502016-07-19 05:10:40 -07001148 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001149 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001150 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001151 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001152 }
reedb2db8982014-11-13 12:41:02 -08001153
Mike Kleine083f7c2018-02-07 12:54:27 -05001154 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001155 if (rec.fSaveLayerFlags & kF16ColorType) {
1156 info = info.makeColorType(kRGBA_F16_SkColorType);
1157 }
reed129ed1c2016-02-22 06:42:31 -08001158
Hal Canary704cd322016-11-07 14:13:52 -05001159 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001160 {
Florin Malita4571e492019-07-16 10:25:58 -04001161 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001162 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001163 const bool trackCoverage =
1164 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001165 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001166 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001167 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001168 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1169 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001170 return;
reed61f501f2015-04-29 08:34:00 -07001171 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001172 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001173 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174
Mike Reedb43a3e02017-02-11 10:18:58 -05001175 // only have a "next" if this new layer doesn't affect the clip (rare)
1176 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 fMCRec->fLayer = layer;
1178 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001179
Mike Reedc61abee2017-02-28 17:45:27 -05001180 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001181 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001182 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001183 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001184
Mike Reedc42a1cd2017-02-14 14:25:14 -05001185 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1186
1187 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1188 if (layer->fNext) {
1189 // need to punch a hole in the previous device, so we don't draw there, given that
1190 // the new top-layer will allow drawing to happen "below" it.
1191 SkRegion hole(ir);
1192 do {
1193 layer = layer->fNext;
1194 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1195 } while (layer->fNext);
1196 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001199int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001200 if (0xFF == alpha) {
1201 return this->saveLayer(bounds, nullptr);
1202 } else {
1203 SkPaint tmpPaint;
1204 tmpPaint.setAlpha(alpha);
1205 return this->saveLayer(bounds, &tmpPaint);
1206 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001207}
1208
Mike Reed148b7fd2018-12-18 17:38:18 -05001209void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001210 SkIRect devBounds;
1211 if (localBounds) {
1212 SkRect tmp;
1213 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1214 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1215 devBounds.setEmpty();
1216 }
1217 } else {
1218 devBounds = this->getDeviceClipBounds();
1219 }
1220 if (devBounds.isEmpty()) {
1221 return;
1222 }
1223
Mike Reed148b7fd2018-12-18 17:38:18 -05001224 SkBaseDevice* device = this->getTopDevice();
1225 if (nullptr == device) { // Do we still need this check???
1226 return;
1227 }
1228
Michael Ludwig915b7792019-10-22 17:40:41 +00001229 // need the bounds relative to the device itself
1230 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
Mike Reed148b7fd2018-12-18 17:38:18 -05001231
Michael Ludwigac352122019-08-28 21:03:05 +00001232 // This is getting the special image from the current device, which is then drawn into (both by
1233 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1234 // own device, we need to explicitly copy the back image contents so that its original content
1235 // is available when we splat it back later during restore.
1236 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001237 if (!backImage) {
1238 return;
1239 }
1240
1241 // we really need the save, so we can wack the fMCRec
1242 this->checkForDeferredSave();
1243
1244 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1245
1246 SkPaint paint;
1247 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001248 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001249}
1250
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251void SkCanvas::internalRestore() {
1252 SkASSERT(fMCStack.count() != 0);
1253
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001254 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 DeviceCM* layer = fMCRec->fLayer; // may be null
1256 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001257 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258
Mike Reed148b7fd2018-12-18 17:38:18 -05001259 // move this out before we do the actual restore
1260 auto backImage = std::move(fMCRec->fBackImage);
1261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 // now do the normal restore()
1263 fMCRec->~MCRec(); // balanced in save()
1264 fMCStack.pop_back();
1265 fMCRec = (MCRec*)fMCStack.back();
1266
Mike Reedc42a1cd2017-02-14 14:25:14 -05001267 if (fMCRec) {
1268 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1269 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001270
Mike Reed148b7fd2018-12-18 17:38:18 -05001271 if (backImage) {
1272 SkPaint paint;
1273 paint.setBlendMode(SkBlendMode::kDstOver);
1274 const int x = backImage->fLoc.x();
1275 const int y = backImage->fLoc.y();
1276 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1277 nullptr, SkMatrix::I());
1278 }
1279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1281 since if we're being recorded, we don't want to record this (the
1282 recorder will have already recorded the restore).
1283 */
bsalomon49f085d2014-09-05 13:34:00 -07001284 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001285 if (fMCRec) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001286 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001287 layer->fDevice->setImmutable();
Michael Ludwig915b7792019-10-22 17:40:41 +00001288 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
1289 layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001290 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001291 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001292 this->internalSetMatrix(layer->fStashedMatrix);
Michael Ludwig915b7792019-10-22 17:40:41 +00001293 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001294 delete layer;
reedb679ca82015-04-07 04:40:48 -07001295 } else {
1296 // we're at the root
reeda499f902015-05-01 09:34:31 -07001297 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001298 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001299 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001301 }
msarettfbfa2582016-08-12 08:29:08 -07001302
1303 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001304 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001305 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1306 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307}
1308
reede8f30622016-03-23 18:59:25 -07001309sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001310 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001311 props = &fProps;
1312 }
1313 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001314}
1315
reede8f30622016-03-23 18:59:25 -07001316sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001317 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001318 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001319}
1320
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001321SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001322 return this->onImageInfo();
1323}
1324
1325SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001326 SkBaseDevice* dev = this->getDevice();
1327 if (dev) {
1328 return dev->imageInfo();
1329 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001330 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001331 }
1332}
1333
brianosman898235c2016-04-06 07:38:23 -07001334bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001335 return this->onGetProps(props);
1336}
1337
1338bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001339 SkBaseDevice* dev = this->getDevice();
1340 if (dev) {
1341 if (props) {
1342 *props = fProps;
1343 }
1344 return true;
1345 } else {
1346 return false;
1347 }
1348}
1349
reed6ceeebd2016-03-09 14:26:26 -08001350bool SkCanvas::peekPixels(SkPixmap* pmap) {
1351 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001352}
1353
reed884e97c2015-05-26 11:31:54 -07001354bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001355 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001356 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001357}
1358
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001359void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001360 SkPixmap pmap;
1361 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001362 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001363 }
1364 if (info) {
1365 *info = pmap.info();
1366 }
1367 if (rowBytes) {
1368 *rowBytes = pmap.rowBytes();
1369 }
1370 if (origin) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001371 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001372 }
reed884e97c2015-05-26 11:31:54 -07001373 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001374}
1375
reed884e97c2015-05-26 11:31:54 -07001376bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001377 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001378 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001379}
1380
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382
Mike Reed8bcd1282019-03-13 16:51:54 -04001383// In our current design/features, we should never have a layer (src) in a different colorspace
1384// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1385// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1386// colorspace.
1387static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1388 SkASSERT(src == dst);
1389}
1390
Michael Ludwig915b7792019-10-22 17:40:41 +00001391void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001392 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001394 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 paint = &tmp;
1396 }
reed@google.com4b226022011-01-11 18:32:13 +00001397
Mike Reed38992392019-07-30 10:48:15 -04001398 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001401 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001402 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1403 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001404 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001405 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwig915b7792019-10-22 17:40:41 +00001406 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001407 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001408 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1409 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001410 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1411 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001412 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1413 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001414 }
reed@google.com76dd2772012-01-05 21:15:07 +00001415 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001416 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001417 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 }
reeda2217ef2016-07-20 06:04:34 -07001419
Mike Reed38992392019-07-30 10:48:15 -04001420 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421}
1422
reed32704672015-12-16 08:27:10 -08001423/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001424
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001425void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001426 if (dx || dy) {
1427 this->checkForDeferredSave();
reedfe69b502016-09-12 06:31:48 -07001428 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001429
reedfe69b502016-09-12 06:31:48 -07001430 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001431 // However, if either is non-finite, we might still complicate the matrix type,
1432 // so we still have to compute this.
1433 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001434
Mike Reedc42a1cd2017-02-14 14:25:14 -05001435 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001436
reedfe69b502016-09-12 06:31:48 -07001437 this->didTranslate(dx,dy);
1438 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439}
1440
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001441void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001442#ifdef SK_SUPPORT_LEGACY_CANVAS_MATRIX_VIRTUALS
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001443 SkMatrix m;
1444 m.setScale(sx, sy);
1445 this->concat(m);
Mike Reed9403c382020-01-13 14:40:56 +00001446#else
1447 if (sx != 1 || sy != 1) {
1448 this->checkForDeferredSave();
1449 fMCRec->fMatrix.preScale(sx, sy);
1450
1451 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1452 // but pre-scaling by a non-finite does change it, so we have to recompute.
1453 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1454
1455 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1456
1457 this->didScale(sx, sy);
1458 }
1459#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460}
1461
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001462void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001463 SkMatrix m;
1464 m.setRotate(degrees);
1465 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
bungeman7438bfc2016-07-12 15:01:19 -07001468void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1469 SkMatrix m;
1470 m.setRotate(degrees, px, py);
1471 this->concat(m);
1472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001475 SkMatrix m;
1476 m.setSkew(sx, sy);
1477 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001478}
1479
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001480void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001481 if (matrix.isIdentity()) {
1482 return;
1483 }
1484
reed2ff1fce2014-12-11 07:07:37 -08001485 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001486 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001487 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001488
Mike Reed7627fa52017-02-08 10:07:53 -05001489 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001490
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001491 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001492}
1493
Mike Reed064c7f92020-01-08 17:33:04 -05001494void SkCanvas::concat44(const SkScalar m[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001495 this->checkForDeferredSave();
1496
Mike Reed064c7f92020-01-08 17:33:04 -05001497 fMCRec->fMatrix.preConcat44(m);
Mike Reed403c8072020-01-08 10:40:39 -05001498
1499 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1500
1501 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1502
1503 this->didConcat44(m);
1504}
Mike Reed064c7f92020-01-08 17:33:04 -05001505
1506void SkCanvas::concat(const SkMatrix44& m) {
1507 this->concat44(m.values());
1508}
Mike Reed403c8072020-01-08 10:40:39 -05001509
reed8c30a812016-04-20 16:36:51 -07001510void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001511 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001512 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001513
Mike Reedc42a1cd2017-02-14 14:25:14 -05001514 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001515}
1516
1517void SkCanvas::setMatrix(const SkMatrix& matrix) {
1518 this->checkForDeferredSave();
1519 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001520 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521}
1522
reed@android.com8a1c16f2008-12-17 15:59:43 +00001523void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001524 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525}
1526
1527//////////////////////////////////////////////////////////////////////////////
1528
Mike Reedc1f77742016-12-09 09:00:50 -05001529void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001530 if (!rect.isFinite()) {
1531 return;
1532 }
reed2ff1fce2014-12-11 07:07:37 -08001533 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001534 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1535 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001536}
1537
Mike Reedc1f77742016-12-09 09:00:50 -05001538void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001539 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001540
Mike Reed7627fa52017-02-08 10:07:53 -05001541 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001542
reedc64eff52015-11-21 12:39:45 -08001543 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001544 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1545 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001546 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547}
1548
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001549void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1550 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001551 if (fClipRestrictionRect.isEmpty()) {
1552 // we notify the device, but we *dont* resolve deferred saves (since we're just
1553 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001554 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001555 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001556 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001557 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001558 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001559 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001560 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1561 }
1562}
1563
Mike Reedc1f77742016-12-09 09:00:50 -05001564void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001565 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001566 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001567 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001568 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1569 } else {
1570 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001571 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001572}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001573
Mike Reedc1f77742016-12-09 09:00:50 -05001574void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001575 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001576
Brian Salomona3b45d42016-10-03 11:36:16 -04001577 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001578
Mike Reed7627fa52017-02-08 10:07:53 -05001579 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001580
Mike Reed20800c82017-11-15 16:09:04 -05001581 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1582 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001583 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001584}
1585
Mike Reedc1f77742016-12-09 09:00:50 -05001586void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001587 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001588 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001589
1590 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1591 SkRect r;
1592 if (path.isRect(&r)) {
1593 this->onClipRect(r, op, edgeStyle);
1594 return;
1595 }
1596 SkRRect rrect;
1597 if (path.isOval(&r)) {
1598 rrect.setOval(r);
1599 this->onClipRRect(rrect, op, edgeStyle);
1600 return;
1601 }
1602 if (path.isRRect(&rrect)) {
1603 this->onClipRRect(rrect, op, edgeStyle);
1604 return;
1605 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001606 }
robertphillips39f05382015-11-24 09:30:12 -08001607
1608 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001609}
1610
Mike Reedc1f77742016-12-09 09:00:50 -05001611void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001612 AutoValidateClip avc(this);
1613
Brian Salomona3b45d42016-10-03 11:36:16 -04001614 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001615
Mike Reed7627fa52017-02-08 10:07:53 -05001616 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617
Brian Salomona3b45d42016-10-03 11:36:16 -04001618 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001619 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001620 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001621 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
Mike Reedc1f77742016-12-09 09:00:50 -05001624void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001625 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627}
1628
Mike Reedc1f77742016-12-09 09:00:50 -05001629void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001630 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001631
reed@google.com5c3d1472011-02-22 19:12:23 +00001632 AutoValidateClip avc(this);
1633
Mike Reed20800c82017-11-15 16:09:04 -05001634 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001635 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636}
1637
reed@google.com819c9212011-02-23 18:56:55 +00001638#ifdef SK_DEBUG
1639void SkCanvas::validateClip() const {
1640 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001641 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001642 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001643 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001644 return;
1645 }
reed@google.com819c9212011-02-23 18:56:55 +00001646}
1647#endif
1648
Mike Reeda1361362017-03-07 09:37:29 -05001649bool SkCanvas::androidFramework_isClipAA() const {
1650 bool containsAA = false;
1651
1652 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1653
1654 return containsAA;
1655}
1656
1657class RgnAccumulator {
1658 SkRegion* fRgn;
1659public:
1660 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1661 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1662 SkIPoint origin = device->getOrigin();
1663 if (origin.x() | origin.y()) {
1664 rgn->translate(origin.x(), origin.y());
1665 }
1666 fRgn->op(*rgn, SkRegion::kUnion_Op);
1667 }
1668};
1669
1670void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1671 RgnAccumulator accum(rgn);
1672 SkRegion tmp;
1673
1674 rgn->setEmpty();
1675 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001676}
1677
reed@google.com5c3d1472011-02-22 19:12:23 +00001678///////////////////////////////////////////////////////////////////////////////
1679
reed@google.com754de5f2014-02-24 19:38:20 +00001680bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001681 return fMCRec->fRasterClip.isEmpty();
1682
1683 // TODO: should we only use the conservative answer in a recording canvas?
1684#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001685 SkBaseDevice* dev = this->getTopDevice();
1686 // if no device we return true
1687 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001688#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001689}
1690
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001691bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001692 SkBaseDevice* dev = this->getTopDevice();
1693 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001694 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001695}
1696
msarettfbfa2582016-08-12 08:29:08 -07001697static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1698#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1699 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1700 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1701 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1702 return 0xF != _mm_movemask_ps(mask);
1703#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1704 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1705 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1706 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1707 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1708#else
1709 SkRect devRectAsRect;
1710 SkRect devClipAsRect;
1711 devRect.store(&devRectAsRect.fLeft);
1712 devClip.store(&devClipAsRect.fLeft);
1713 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1714#endif
1715}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001716
msarettfbfa2582016-08-12 08:29:08 -07001717// It's important for this function to not be inlined. Otherwise the compiler will share code
1718// between the fast path and the slow path, resulting in two slow paths.
1719static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1720 const SkMatrix& matrix) {
1721 SkRect deviceRect;
1722 matrix.mapRect(&deviceRect, src);
1723 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1724}
1725
1726bool SkCanvas::quickReject(const SkRect& src) const {
1727#ifdef SK_DEBUG
1728 // Verify that fDeviceClipBounds are set properly.
1729 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001730 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001731 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001732 } else {
msarettfbfa2582016-08-12 08:29:08 -07001733 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734 }
msarettfbfa2582016-08-12 08:29:08 -07001735
msarett9637ea92016-08-18 14:03:30 -07001736 // Verify that fIsScaleTranslate is set properly.
1737 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001738#endif
1739
msarett9637ea92016-08-18 14:03:30 -07001740 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001741 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1742 }
1743
1744 // We inline the implementation of mapScaleTranslate() for the fast path.
1745 float sx = fMCRec->fMatrix.getScaleX();
1746 float sy = fMCRec->fMatrix.getScaleY();
1747 float tx = fMCRec->fMatrix.getTranslateX();
1748 float ty = fMCRec->fMatrix.getTranslateY();
1749 Sk4f scale(sx, sy, sx, sy);
1750 Sk4f trans(tx, ty, tx, ty);
1751
1752 // Apply matrix.
1753 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1754
1755 // Make sure left < right, top < bottom.
1756 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1757 Sk4f min = Sk4f::Min(ltrb, rblt);
1758 Sk4f max = Sk4f::Max(ltrb, rblt);
1759 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1760 // ARM this sequence generates the fastest (a single instruction).
1761 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1762
1763 // Check if the device rect is NaN or outside the clip.
1764 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
reed@google.com3b3e8952012-08-16 20:53:31 +00001767bool SkCanvas::quickReject(const SkPath& path) const {
1768 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769}
1770
Mike Klein83c8dd92017-11-28 17:08:45 -05001771SkRect SkCanvas::getLocalClipBounds() const {
1772 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001773 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001774 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 }
1776
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001777 SkMatrix inverse;
1778 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001779 if (!fMCRec->fMatrix.invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001780 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001781 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782
Mike Reed42e8c532017-01-23 14:09:13 -05001783 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001784 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001785 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001786
Mike Reedb57b9312018-04-23 12:12:54 -04001787 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001788 inverse.mapRect(&bounds, r);
1789 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790}
1791
Mike Klein83c8dd92017-11-28 17:08:45 -05001792SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001793 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001794}
1795
Mike Reed403c8072020-01-08 10:40:39 -05001796SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001797 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798}
1799
Mike Reed75435872020-01-13 21:15:06 -05001800SkM44 SkCanvas::getTotalM44() const {
1801 return fMCRec->fMatrix;
1802}
1803
Brian Osman11052242016-10-27 14:47:55 -04001804GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001805 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001806 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001807}
1808
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001809GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001810 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001811 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001812}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001813
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001814void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1815 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001816 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001817 if (outer.isEmpty()) {
1818 return;
1819 }
1820 if (inner.isEmpty()) {
1821 this->drawRRect(outer, paint);
1822 return;
1823 }
1824
1825 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001826 // be able to return ...
1827 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001828 //
1829 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001830 if (!outer.getBounds().contains(inner.getBounds())) {
1831 return;
1832 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001833
1834 this->onDrawDRRect(outer, inner, paint);
1835}
1836
reed41af9662015-01-05 07:49:08 -08001837void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001838 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001839 this->onDrawPaint(paint);
1840}
1841
1842void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001843 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001844 // To avoid redundant logic in our culling code and various backends, we always sort rects
1845 // before passing them along.
1846 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001847}
1848
Mike Reedd5674082019-04-19 15:00:47 -04001849void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1850 TRACE_EVENT0("skia", TRACE_FUNC);
1851 this->onDrawBehind(paint);
1852}
1853
msarettdca352e2016-08-26 06:37:45 -07001854void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001855 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001856 if (region.isEmpty()) {
1857 return;
1858 }
1859
1860 if (region.isRect()) {
1861 return this->drawIRect(region.getBounds(), paint);
1862 }
1863
1864 this->onDrawRegion(region, paint);
1865}
1866
reed41af9662015-01-05 07:49:08 -08001867void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001868 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001869 // To avoid redundant logic in our culling code and various backends, we always sort rects
1870 // before passing them along.
1871 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001872}
1873
1874void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001875 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001876 this->onDrawRRect(rrect, paint);
1877}
1878
1879void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001880 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001881 this->onDrawPoints(mode, count, pts, paint);
1882}
1883
Mike Reede88a1cb2017-03-17 09:50:46 -04001884void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1885 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001886 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001887 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001888 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1889 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001890 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001891}
1892
1893void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001894 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001895 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001896 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1897}
1898
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001899void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1900 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001901 TRACE_EVENT0("skia", TRACE_FUNC);
1902 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001903 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001904 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1905}
1906
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001907void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1908 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001909 TRACE_EVENT0("skia", TRACE_FUNC);
1910 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001911 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001912 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001913}
1914
1915void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001916 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001917 this->onDrawPath(path, paint);
1918}
1919
reeda85d4d02015-05-06 12:56:48 -07001920void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001921 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001922 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001923 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001924}
1925
Mike Reedc4e31092018-01-30 11:15:27 -05001926// Returns true if the rect can be "filled" : non-empty and finite
1927static bool fillable(const SkRect& r) {
1928 SkScalar w = r.width();
1929 SkScalar h = r.height();
1930 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1931}
1932
reede47829b2015-08-06 10:02:53 -07001933void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1934 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001935 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001936 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001937 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001938 return;
1939 }
1940 this->onDrawImageRect(image, &src, dst, paint, constraint);
1941}
reed41af9662015-01-05 07:49:08 -08001942
reed84984ef2015-07-17 07:09:43 -07001943void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1944 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001945 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001946 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001947}
1948
Brian Salomonf08002c2018-10-26 16:15:46 -04001949void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001950 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001951 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001952 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001953}
reede47829b2015-08-06 10:02:53 -07001954
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001955namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001956class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001957public:
Brian Salomon969be1c2018-05-21 14:37:49 -04001958 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
1959 if (!origPaint) {
1960 return;
1961 }
1962 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
1963 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
1964 }
1965 if (origPaint->getMaskFilter()) {
1966 fPaint.writable()->setMaskFilter(nullptr);
1967 }
1968 if (origPaint->isAntiAlias()) {
1969 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001970 }
1971 }
1972
1973 const SkPaint* get() const {
1974 return fPaint;
1975 }
1976
1977private:
Brian Salomon969be1c2018-05-21 14:37:49 -04001978 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001979};
1980} // namespace
1981
reed4c21dc52015-06-25 12:32:03 -07001982void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1983 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001984 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001985 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001986 if (dst.isEmpty()) {
1987 return;
1988 }
msarett552bca92016-08-03 06:53:26 -07001989 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001990 LatticePaint latticePaint(paint);
1991 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07001992 } else {
reede47829b2015-08-06 10:02:53 -07001993 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001994 }
reed4c21dc52015-06-25 12:32:03 -07001995}
1996
msarett16882062016-08-16 09:31:08 -07001997void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1998 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001999 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002000 RETURN_ON_NULL(image);
2001 if (dst.isEmpty()) {
2002 return;
2003 }
msarett71df2d72016-09-30 12:41:42 -07002004
2005 SkIRect bounds;
2006 Lattice latticePlusBounds = lattice;
2007 if (!latticePlusBounds.fBounds) {
2008 bounds = SkIRect::MakeWH(image->width(), image->height());
2009 latticePlusBounds.fBounds = &bounds;
2010 }
2011
2012 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002013 LatticePaint latticePaint(paint);
2014 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002015 } else {
2016 this->drawImageRect(image, dst, paint);
2017 }
2018}
2019
reed41af9662015-01-05 07:49:08 -08002020void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002021 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002022 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002023 return;
2024 }
reed41af9662015-01-05 07:49:08 -08002025 this->onDrawBitmap(bitmap, dx, dy, paint);
2026}
2027
reede47829b2015-08-06 10:02:53 -07002028void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002029 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002030 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002031 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002032 return;
2033 }
reede47829b2015-08-06 10:02:53 -07002034 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002035}
2036
reed84984ef2015-07-17 07:09:43 -07002037void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2038 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002039 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002040}
2041
reede47829b2015-08-06 10:02:53 -07002042void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2043 SrcRectConstraint constraint) {
2044 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2045 constraint);
2046}
reede47829b2015-08-06 10:02:53 -07002047
reed41af9662015-01-05 07:49:08 -08002048void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2049 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002050 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002051 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002052 return;
2053 }
msarett552bca92016-08-03 06:53:26 -07002054 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002055 LatticePaint latticePaint(paint);
2056 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002057 } else {
reeda5517e22015-07-14 10:54:12 -07002058 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002059 }
reed41af9662015-01-05 07:49:08 -08002060}
2061
msarettc573a402016-08-02 08:05:56 -07002062void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2063 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002064 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002065 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002066 return;
2067 }
msarett71df2d72016-09-30 12:41:42 -07002068
2069 SkIRect bounds;
2070 Lattice latticePlusBounds = lattice;
2071 if (!latticePlusBounds.fBounds) {
2072 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2073 latticePlusBounds.fBounds = &bounds;
2074 }
2075
2076 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002077 LatticePaint latticePaint(paint);
2078 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002079 } else {
msarett16882062016-08-16 09:31:08 -07002080 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002081 }
msarettc573a402016-08-02 08:05:56 -07002082}
2083
reed71c3c762015-06-24 10:29:17 -07002084void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002085 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002086 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002087 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002088 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002089 if (count <= 0) {
2090 return;
2091 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002092 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002093 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002094 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002095}
2096
reedf70b5312016-03-04 16:36:20 -08002097void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002098 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002099 if (key) {
2100 this->onDrawAnnotation(rect, key, value);
2101 }
2102}
2103
reede47829b2015-08-06 10:02:53 -07002104void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2105 const SkPaint* paint, SrcRectConstraint constraint) {
2106 if (src) {
2107 this->drawImageRect(image, *src, dst, paint, constraint);
2108 } else {
2109 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2110 dst, paint, constraint);
2111 }
2112}
2113void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2114 const SkPaint* paint, SrcRectConstraint constraint) {
2115 if (src) {
2116 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2117 } else {
2118 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2119 dst, paint, constraint);
2120 }
2121}
2122
Mike Reed4204da22017-05-17 08:53:36 -04002123void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002124 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002125 this->onDrawShadowRec(path, rec);
2126}
2127
2128void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2129 SkPaint paint;
2130 const SkRect& pathBounds = path.getBounds();
2131
Mike Reed38992392019-07-30 10:48:15 -04002132 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002133 while (iter.next()) {
2134 iter.fDevice->drawShadow(path, rec);
2135 }
Mike Reed38992392019-07-30 10:48:15 -04002136 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002137}
2138
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002139void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002140 QuadAAFlags aaFlags, const SkColor4f& color,
2141 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002142 TRACE_EVENT0("skia", TRACE_FUNC);
2143 // Make sure the rect is sorted before passing it along
2144 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2145}
2146
2147void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2148 const SkPoint dstClips[],
2149 const SkMatrix preViewMatrices[],
2150 const SkPaint* paint,
2151 SrcRectConstraint constraint) {
2152 TRACE_EVENT0("skia", TRACE_FUNC);
2153 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2154}
2155
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156//////////////////////////////////////////////////////////////////////////////
2157// These are the virtual drawing methods
2158//////////////////////////////////////////////////////////////////////////////
2159
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002160void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002161 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002162 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2163 }
2164}
2165
reed41af9662015-01-05 07:49:08 -08002166void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002167 this->internalDrawPaint(paint);
2168}
2169
2170void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002171 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172
2173 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002174 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175 }
2176
Mike Reed38992392019-07-30 10:48:15 -04002177 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178}
2179
reed41af9662015-01-05 07:49:08 -08002180void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2181 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182 if ((long)count <= 0) {
2183 return;
2184 }
2185
Mike Reed822128b2017-02-28 16:41:03 -05002186 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002187 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002188 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002189 // special-case 2 points (common for drawing a single line)
2190 if (2 == count) {
2191 r.set(pts[0], pts[1]);
2192 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002193 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002194 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002195 if (!r.isFinite()) {
2196 return;
2197 }
Mike Reed822128b2017-02-28 16:41:03 -05002198 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002199 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2200 return;
2201 }
2202 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002203 }
reed@google.coma584aed2012-05-16 14:06:02 +00002204
halcanary96fcdcc2015-08-27 07:41:13 -07002205 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206
Mike Reed38992392019-07-30 10:48:15 -04002207 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002208
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002210 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 }
reed@google.com4b226022011-01-11 18:32:13 +00002212
Mike Reed38992392019-07-30 10:48:15 -04002213 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214}
2215
reed4a167172016-08-18 17:15:25 -07002216static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002217 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002218}
2219
reed41af9662015-01-05 07:49:08 -08002220void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002221 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002223 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002224 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002225 return;
2226 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227 }
reed@google.com4b226022011-01-11 18:32:13 +00002228
reed4a167172016-08-18 17:15:25 -07002229 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002230 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231
reed4a167172016-08-18 17:15:25 -07002232 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002233 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002234 }
2235
Mike Reed38992392019-07-30 10:48:15 -04002236 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002237 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002238 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002239 SkDrawIter iter(this);
2240 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002241 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002242 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244}
2245
msarett44df6512016-08-25 13:54:30 -07002246void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002247 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002248 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002249 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002250 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2251 return;
2252 }
msarett44df6512016-08-25 13:54:30 -07002253 }
2254
Mike Reed38992392019-07-30 10:48:15 -04002255 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002256
2257 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002258 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002259 }
2260
Mike Reed38992392019-07-30 10:48:15 -04002261 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002262}
2263
Mike Reedd5674082019-04-19 15:00:47 -04002264void SkCanvas::onDrawBehind(const SkPaint& paint) {
2265 SkIRect bounds;
2266 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2267 for (;;) {
2268 const MCRec* rec = (const MCRec*)iter.prev();
2269 if (!rec) {
2270 return; // no backimages, so nothing to draw
2271 }
2272 if (rec->fBackImage) {
2273 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2274 rec->fBackImage->fImage->width(),
2275 rec->fBackImage->fImage->height());
2276 break;
2277 }
2278 }
2279
Mike Reed38992392019-07-30 10:48:15 -04002280 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002281
2282 while (iter.next()) {
2283 SkBaseDevice* dev = iter.fDevice;
2284
Mike Reedd5674082019-04-19 15:00:47 -04002285 dev->save();
2286 // We use clipRegion because it is already defined to operate in dev-space
2287 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2288 // but we don't want that, so we undo that before calling in.
Michael Ludwig915b7792019-10-22 17:40:41 +00002289 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002290 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002291 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002292 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002293 }
2294
Mike Reed38992392019-07-30 10:48:15 -04002295 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002296}
2297
reed41af9662015-01-05 07:49:08 -08002298void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002299 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002300 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002301 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002302 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002303 return;
2304 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002305 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002306
Mike Reed38992392019-07-30 10:48:15 -04002307 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002308
2309 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002310 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002311 }
2312
Mike Reed38992392019-07-30 10:48:15 -04002313 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002314}
2315
bsalomonac3aa242016-08-19 11:25:19 -07002316void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2317 SkScalar sweepAngle, bool useCenter,
2318 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002319 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002320 if (paint.canComputeFastBounds()) {
2321 SkRect storage;
2322 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002323 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002324 return;
2325 }
bsalomonac3aa242016-08-19 11:25:19 -07002326 }
2327
Mike Reed38992392019-07-30 10:48:15 -04002328 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002329
2330 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002331 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002332 }
2333
Mike Reed38992392019-07-30 10:48:15 -04002334 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002335}
2336
reed41af9662015-01-05 07:49:08 -08002337void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002338 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002339 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002340 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2341 return;
2342 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002343 }
2344
2345 if (rrect.isRect()) {
2346 // call the non-virtual version
2347 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002348 return;
2349 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002350 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002351 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2352 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002353 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002354
Mike Reed38992392019-07-30 10:48:15 -04002355 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002356
2357 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002358 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002359 }
2360
Mike Reed38992392019-07-30 10:48:15 -04002361 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002362}
2363
Mike Reed822128b2017-02-28 16:41:03 -05002364void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002365 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002366 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002367 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2368 return;
2369 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002370 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002371
Mike Reed38992392019-07-30 10:48:15 -04002372 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002373
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002374 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002375 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002376 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002377
Mike Reed38992392019-07-30 10:48:15 -04002378 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002379}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002380
reed41af9662015-01-05 07:49:08 -08002381void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002382 if (!path.isFinite()) {
2383 return;
2384 }
2385
Mike Reed822128b2017-02-28 16:41:03 -05002386 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002387 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002388 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002389 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2390 return;
2391 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002393
Mike Reed822128b2017-02-28 16:41:03 -05002394 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002395 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002396 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002397 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002398 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002399 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400
Mike Reed38992392019-07-30 10:48:15 -04002401 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402
2403 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002404 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 }
2406
Mike Reed38992392019-07-30 10:48:15 -04002407 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408}
2409
reed262a71b2015-12-05 13:07:27 -08002410bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002411 if (!paint.getImageFilter()) {
2412 return false;
2413 }
2414
2415 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002416 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002417 return false;
2418 }
2419
2420 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2421 // Once we can filter and the filter will return a result larger than itself, we should be
2422 // able to remove this constraint.
2423 // skbug.com/4526
2424 //
2425 SkPoint pt;
2426 ctm.mapXY(x, y, &pt);
2427 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2428 return ir.contains(fMCRec->fRasterClip.getBounds());
2429}
2430
Mike Reedf441cfc2018-04-11 14:50:16 -04002431// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2432// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2433// null.
2434static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2435 if (paintParam) {
2436 *real = *paintParam;
2437 real->setStyle(SkPaint::kFill_Style);
2438 real->setPathEffect(nullptr);
2439 paintParam = real;
2440 }
2441 return paintParam;
2442}
2443
reeda85d4d02015-05-06 12:56:48 -07002444void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002445 SkPaint realPaint;
2446 paint = init_image_paint(&realPaint, paint);
2447
reeda85d4d02015-05-06 12:56:48 -07002448 SkRect bounds = SkRect::MakeXYWH(x, y,
2449 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002450 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002451 SkRect tmp = bounds;
2452 if (paint) {
2453 paint->computeFastBounds(tmp, &tmp);
2454 }
2455 if (this->quickReject(tmp)) {
2456 return;
2457 }
reeda85d4d02015-05-06 12:56:48 -07002458 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002459 // At this point we need a real paint object. If the caller passed null, then we should
2460 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2461 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2462 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002463
reeda2217ef2016-07-20 06:04:34 -07002464 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002465 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2466 *paint);
2467 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002468 special = this->getDevice()->makeSpecial(image);
2469 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002470 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002471 }
2472 }
2473
Mike Reed38992392019-07-30 10:48:15 -04002474 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002475
reeda85d4d02015-05-06 12:56:48 -07002476 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002477 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002478 if (special) {
2479 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002480 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002481 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002482 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002483 SkScalarRoundToInt(pt.fY), pnt,
2484 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002485 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002486 iter.fDevice->drawImageRect(
2487 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2488 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002489 }
reeda85d4d02015-05-06 12:56:48 -07002490 }
halcanary9d524f22016-03-29 09:03:52 -07002491
Mike Reed38992392019-07-30 10:48:15 -04002492 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002493}
2494
reed41af9662015-01-05 07:49:08 -08002495void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002496 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002497 SkPaint realPaint;
2498 paint = init_image_paint(&realPaint, paint);
2499
halcanary96fcdcc2015-08-27 07:41:13 -07002500 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002501 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002502 if (paint) {
2503 paint->computeFastBounds(dst, &storage);
2504 }
2505 if (this->quickReject(storage)) {
2506 return;
2507 }
reeda85d4d02015-05-06 12:56:48 -07002508 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002509 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002510
Mike Reed38992392019-07-30 10:48:15 -04002511 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002512
reeda85d4d02015-05-06 12:56:48 -07002513 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002514 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002515 }
halcanary9d524f22016-03-29 09:03:52 -07002516
Mike Reed38992392019-07-30 10:48:15 -04002517 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002518}
2519
reed41af9662015-01-05 07:49:08 -08002520void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521 SkDEBUGCODE(bitmap.validate();)
2522
reed33366972015-10-08 09:22:02 -07002523 if (bitmap.drawsNothing()) {
2524 return;
2525 }
2526
Mike Reedf441cfc2018-04-11 14:50:16 -04002527 SkPaint realPaint;
2528 init_image_paint(&realPaint, paint);
2529 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002530
Mike Reed822128b2017-02-28 16:41:03 -05002531 SkRect bounds;
2532 bitmap.getBounds(&bounds);
2533 bounds.offset(x, y);
2534 bool canFastBounds = paint->canComputeFastBounds();
2535 if (canFastBounds) {
2536 SkRect storage;
2537 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002538 return;
2539 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002540 }
reed@google.com4b226022011-01-11 18:32:13 +00002541
reeda2217ef2016-07-20 06:04:34 -07002542 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002543 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2544 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002545 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002546 special = this->getDevice()->makeSpecial(bitmap);
2547 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002548 drawAsSprite = false;
2549 }
2550 }
2551
Mike Reed38992392019-07-30 10:48:15 -04002552 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002553
2554 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002555 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002556 if (special) {
reed262a71b2015-12-05 13:07:27 -08002557 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002558 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002559 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002560 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002561 SkScalarRoundToInt(pt.fY), pnt,
2562 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002563 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002564 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2565 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2566 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002567 }
reed33366972015-10-08 09:22:02 -07002568 }
msarettfbfa2582016-08-12 08:29:08 -07002569
Mike Reed38992392019-07-30 10:48:15 -04002570 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571}
2572
reed@google.com9987ec32011-09-07 11:57:52 +00002573// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002574void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002575 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002576 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002577 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002578 return;
2579 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002580
halcanary96fcdcc2015-08-27 07:41:13 -07002581 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002582 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002583 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2584 return;
2585 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002586 }
reed@google.com3d608122011-11-21 15:16:16 +00002587
reed@google.com33535f32012-09-25 15:37:50 +00002588 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002589 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002590 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002591 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002592
Mike Reed38992392019-07-30 10:48:15 -04002593 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002594
reed@google.com33535f32012-09-25 15:37:50 +00002595 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002596 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002597 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002598
Mike Reed38992392019-07-30 10:48:15 -04002599 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002600}
2601
reed41af9662015-01-05 07:49:08 -08002602void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002603 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002604 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002605 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002606}
2607
reed4c21dc52015-06-25 12:32:03 -07002608void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2609 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002610 SkPaint realPaint;
2611 paint = init_image_paint(&realPaint, paint);
2612
halcanary96fcdcc2015-08-27 07:41:13 -07002613 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002614 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002615 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2616 return;
2617 }
reed@google.com3d608122011-11-21 15:16:16 +00002618 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002619 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002620
Mike Reed38992392019-07-30 10:48:15 -04002621 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002622
reed4c21dc52015-06-25 12:32:03 -07002623 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002624 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002625 }
halcanary9d524f22016-03-29 09:03:52 -07002626
Mike Reed38992392019-07-30 10:48:15 -04002627 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002628}
2629
reed41af9662015-01-05 07:49:08 -08002630void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2631 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002632 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002633 SkPaint realPaint;
2634 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002635
halcanary96fcdcc2015-08-27 07:41:13 -07002636 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002637 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002638 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2639 return;
2640 }
reed4c21dc52015-06-25 12:32:03 -07002641 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002642 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002643
Mike Reed38992392019-07-30 10:48:15 -04002644 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002645
reed4c21dc52015-06-25 12:32:03 -07002646 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002647 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002648 }
halcanary9d524f22016-03-29 09:03:52 -07002649
Mike Reed38992392019-07-30 10:48:15 -04002650 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002651}
2652
msarett16882062016-08-16 09:31:08 -07002653void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2654 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002655 SkPaint realPaint;
2656 paint = init_image_paint(&realPaint, paint);
2657
msarett16882062016-08-16 09:31:08 -07002658 if (nullptr == paint || paint->canComputeFastBounds()) {
2659 SkRect storage;
2660 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2661 return;
2662 }
2663 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002664 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002665
Mike Reed38992392019-07-30 10:48:15 -04002666 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002667
2668 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002669 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002670 }
2671
Mike Reed38992392019-07-30 10:48:15 -04002672 DRAW_END
msarett16882062016-08-16 09:31:08 -07002673}
2674
2675void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2676 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002677 SkPaint realPaint;
2678 paint = init_image_paint(&realPaint, paint);
2679
msarett16882062016-08-16 09:31:08 -07002680 if (nullptr == paint || paint->canComputeFastBounds()) {
2681 SkRect storage;
2682 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2683 return;
2684 }
2685 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002686 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002687
Mike Reed38992392019-07-30 10:48:15 -04002688 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002689
2690 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002691 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002692 }
2693
Mike Reed38992392019-07-30 10:48:15 -04002694 DRAW_END
msarett16882062016-08-16 09:31:08 -07002695}
2696
fmalita00d5c2c2014-08-21 08:53:26 -07002697void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2698 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002699 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002700 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002701 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002702 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002703 SkRect tmp;
2704 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2705 return;
2706 }
2707 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002708 }
2709
fmalita024f9962015-03-03 19:08:17 -08002710 // We cannot filter in the looper as we normally do, because the paint is
2711 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002712 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002713
fmalitaaa1b9122014-08-28 14:32:24 -07002714 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002715 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002716 }
2717
Mike Reed38992392019-07-30 10:48:15 -04002718 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002719}
2720
Mike Reed358fcad2018-11-23 15:27:51 -05002721// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002722void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002723 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2724 TRACE_EVENT0("skia", TRACE_FUNC);
2725 if (byteLength) {
2726 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002727 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002728 }
2729}
Mike Reed4f81bb72019-01-23 09:23:00 -05002730
fmalita00d5c2c2014-08-21 08:53:26 -07002731void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2732 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002733 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002734 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002735 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002736 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002737}
reed@google.come0d9ce82014-04-23 04:00:17 +00002738
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002739void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002740 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002741 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002742
2743 while (iter.next()) {
2744 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002745 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002746 }
2747
Mike Reed38992392019-07-30 10:48:15 -04002748 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002749}
2750
dandovb3c9d1c2014-08-12 08:34:29 -07002751void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002752 const SkPoint texCoords[4], SkBlendMode bmode,
2753 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002754 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002755 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002756 return;
2757 }
mtklein6cfa73a2014-08-13 13:33:49 -07002758
Mike Reedfaba3712016-11-03 14:45:31 -04002759 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002760}
2761
2762void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002763 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002764 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002765 // Since a patch is always within the convex hull of the control points, we discard it when its
2766 // bounding rectangle is completely outside the current clip.
2767 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002768 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002769 if (this->quickReject(bounds)) {
2770 return;
2771 }
mtklein6cfa73a2014-08-13 13:33:49 -07002772
Mike Reed38992392019-07-30 10:48:15 -04002773 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002774
dandovecfff212014-08-04 10:02:00 -07002775 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002776 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002777 }
mtklein6cfa73a2014-08-13 13:33:49 -07002778
Mike Reed38992392019-07-30 10:48:15 -04002779 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002780}
2781
reeda8db7282015-07-07 10:22:31 -07002782void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002783#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002784 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002785#endif
reede3b38ce2016-01-08 09:18:44 -08002786 RETURN_ON_NULL(dr);
2787 if (x || y) {
2788 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2789 this->onDrawDrawable(dr, &matrix);
2790 } else {
2791 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002792 }
2793}
2794
reeda8db7282015-07-07 10:22:31 -07002795void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002796#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002797 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002798#endif
reede3b38ce2016-01-08 09:18:44 -08002799 RETURN_ON_NULL(dr);
2800 if (matrix && matrix->isIdentity()) {
2801 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002802 }
reede3b38ce2016-01-08 09:18:44 -08002803 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002804}
2805
2806void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002807 // drawable bounds are no longer reliable (e.g. android displaylist)
2808 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002809 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002810}
2811
reed71c3c762015-06-24 10:29:17 -07002812void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002813 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002814 const SkRect* cull, const SkPaint* paint) {
2815 if (cull && this->quickReject(*cull)) {
2816 return;
2817 }
2818
2819 SkPaint pnt;
2820 if (paint) {
2821 pnt = *paint;
2822 }
halcanary9d524f22016-03-29 09:03:52 -07002823
Mike Reed38992392019-07-30 10:48:15 -04002824 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002825 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002826 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002827 }
Mike Reed38992392019-07-30 10:48:15 -04002828 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002829}
2830
reedf70b5312016-03-04 16:36:20 -08002831void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2832 SkASSERT(key);
2833
2834 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002835 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002836 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002837 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002838 }
Mike Reed38992392019-07-30 10:48:15 -04002839 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002840}
2841
Michael Ludwiga595f862019-08-27 15:25:49 -04002842void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2843 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002844 SkASSERT(r.isSorted());
2845
2846 // If this used a paint, it would be a filled color with blend mode, which does not
2847 // need to use an autodraw loop, so use SkDrawIter directly.
2848 if (this->quickReject(r)) {
2849 return;
2850 }
2851
Michael Ludwiga4b44882019-08-28 14:34:58 -04002852 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002853 SkDrawIter iter(this);
2854 while(iter.next()) {
2855 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2856 }
2857}
2858
2859void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2860 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2861 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002862 if (count <= 0) {
2863 // Nothing to draw
2864 return;
2865 }
2866
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002867 SkPaint realPaint;
2868 init_image_paint(&realPaint, paint);
2869
Michael Ludwiga4b44882019-08-28 14:34:58 -04002870 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2871 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2872 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2873 // or we need it for the autolooper (since it greatly improves image filter perf).
2874 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2875 bool setBoundsValid = count == 1 || needsAutoLooper;
2876 SkRect setBounds = imageSet[0].fDstRect;
2877 if (imageSet[0].fMatrixIndex >= 0) {
2878 // Account for the per-entry transform that is applied prior to the CTM when drawing
2879 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002880 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002881 if (needsAutoLooper) {
2882 for (int i = 1; i < count; ++i) {
2883 SkRect entryBounds = imageSet[i].fDstRect;
2884 if (imageSet[i].fMatrixIndex >= 0) {
2885 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2886 }
2887 setBounds.joinPossiblyEmptyRect(entryBounds);
2888 }
2889 }
2890
2891 // If we happen to have the draw bounds, though, might as well check quickReject().
2892 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2893 SkRect tmp;
2894 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2895 return;
2896 }
2897 }
2898
2899 if (needsAutoLooper) {
2900 SkASSERT(setBoundsValid);
2901 DRAW_BEGIN(realPaint, &setBounds)
2902 while (iter.next()) {
2903 iter.fDevice->drawEdgeAAImageSet(
2904 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2905 }
2906 DRAW_END
2907 } else {
2908 this->predrawNotify();
2909 SkDrawIter iter(this);
2910 while(iter.next()) {
2911 iter.fDevice->drawEdgeAAImageSet(
2912 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2913 }
2914 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002915}
2916
reed@android.com8a1c16f2008-12-17 15:59:43 +00002917//////////////////////////////////////////////////////////////////////////////
2918// These methods are NOT virtual, and therefore must call back into virtual
2919// methods, rather than actually drawing themselves.
2920//////////////////////////////////////////////////////////////////////////////
2921
reed374772b2016-10-05 17:33:02 -07002922void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002923 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002924 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002925 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002926 this->drawPaint(paint);
2927}
2928
2929void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002930 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002931 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2932}
2933
Mike Reed3661bc92017-02-22 13:21:42 -05002934void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002936 pts[0].set(x0, y0);
2937 pts[1].set(x1, y1);
2938 this->drawPoints(kLines_PointMode, 2, pts, paint);
2939}
2940
Mike Reed3661bc92017-02-22 13:21:42 -05002941void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002942 if (radius < 0) {
2943 radius = 0;
2944 }
2945
2946 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002947 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002948 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949}
2950
2951void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2952 const SkPaint& paint) {
2953 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002954 SkRRect rrect;
2955 rrect.setRectXY(r, rx, ry);
2956 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002957 } else {
2958 this->drawRect(r, paint);
2959 }
2960}
2961
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2963 SkScalar sweepAngle, bool useCenter,
2964 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002965 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002966 if (oval.isEmpty() || !sweepAngle) {
2967 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002968 }
bsalomon21af9ca2016-08-25 12:29:23 -07002969 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002970}
2971
reed@android.comf76bacf2009-05-13 14:00:33 +00002972///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002973#ifdef SK_DISABLE_SKPICTURE
2974void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002975
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002976
2977void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2978 const SkPaint* paint) {}
2979#else
Mike Klein88d90712018-01-27 17:30:04 +00002980/**
2981 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2982 * against the playback cost of recursing into the subpicture to get at its actual ops.
2983 *
2984 * For now we pick a conservatively small value, though measurement (and other heuristics like
2985 * the type of ops contained) may justify changing this value.
2986 */
2987#define kMaxPictureOpsToUnrollInsteadOfRef 1
2988
reedd5fa1a42014-08-09 11:08:05 -07002989void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002990 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002991 RETURN_ON_NULL(picture);
2992
reede3b38ce2016-01-08 09:18:44 -08002993 if (matrix && matrix->isIdentity()) {
2994 matrix = nullptr;
2995 }
Mike Klein88d90712018-01-27 17:30:04 +00002996 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2997 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2998 picture->playback(this);
2999 } else {
3000 this->onDrawPicture(picture, matrix, paint);
3001 }
reedd5fa1a42014-08-09 11:08:05 -07003002}
robertphillips9b14f262014-06-04 05:40:44 -07003003
reedd5fa1a42014-08-09 11:08:05 -07003004void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3005 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003006 if (!paint || paint->canComputeFastBounds()) {
3007 SkRect bounds = picture->cullRect();
3008 if (paint) {
3009 paint->computeFastBounds(bounds, &bounds);
3010 }
3011 if (matrix) {
3012 matrix->mapRect(&bounds);
3013 }
3014 if (this->quickReject(bounds)) {
3015 return;
3016 }
3017 }
3018
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003019 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003020 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003022#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003023
reed@android.com8a1c16f2008-12-17 15:59:43 +00003024///////////////////////////////////////////////////////////////////////////////
3025///////////////////////////////////////////////////////////////////////////////
3026
reed3aafe112016-08-18 12:45:34 -07003027SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003028 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003029
3030 SkASSERT(canvas);
3031
reed3aafe112016-08-18 12:45:34 -07003032 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003033 fDone = !fImpl->next();
3034}
3035
3036SkCanvas::LayerIter::~LayerIter() {
3037 fImpl->~SkDrawIter();
3038}
3039
3040void SkCanvas::LayerIter::next() {
3041 fDone = !fImpl->next();
3042}
3043
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003044SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003045 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003046}
3047
3048const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003049 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003050}
3051
3052const SkPaint& SkCanvas::LayerIter::paint() const {
3053 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003054 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003055 paint = &fDefaultPaint;
3056 }
3057 return *paint;
3058}
3059
Mike Reedca37f322018-03-08 13:22:16 -05003060SkIRect SkCanvas::LayerIter::clipBounds() const {
3061 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003062}
3063
Michael Ludwig915b7792019-10-22 17:40:41 +00003064int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3065int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003066
3067///////////////////////////////////////////////////////////////////////////////
3068
Brian Osmane8a98632019-04-10 10:26:10 -04003069SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3070SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3071SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3072SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3073
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003074SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3075 const SkRect& dstRect, int matrixIndex, float alpha,
3076 unsigned aaFlags, bool hasClip)
3077 : fImage(std::move(image))
3078 , fSrcRect(srcRect)
3079 , fDstRect(dstRect)
3080 , fMatrixIndex(matrixIndex)
3081 , fAlpha(alpha)
3082 , fAAFlags(aaFlags)
3083 , fHasClip(hasClip) {}
3084
3085SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3086 const SkRect& dstRect, float alpha, unsigned aaFlags)
3087 : fImage(std::move(image))
3088 , fSrcRect(srcRect)
3089 , fDstRect(dstRect)
3090 , fAlpha(alpha)
3091 , fAAFlags(aaFlags) {}
3092
3093///////////////////////////////////////////////////////////////////////////////
3094
Mike Reed5df49342016-11-12 08:06:55 -06003095std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003096 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003097 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003098 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003099 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003100
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003101 SkBitmap bitmap;
3102 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003103 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003104 }
Mike Reed12f77342017-11-08 11:19:52 -05003105
3106 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003107 std::make_unique<SkCanvas>(bitmap, *props) :
3108 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003109}
reedd5fa1a42014-08-09 11:08:05 -07003110
3111///////////////////////////////////////////////////////////////////////////////
3112
Florin Malitaee424ac2016-12-01 12:47:59 -05003113SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003114 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003115
Florin Malita439ace92016-12-02 12:05:41 -05003116SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003117 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003118
Herb Derbyefe39bc2018-05-01 17:06:20 -04003119SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003120 : INHERITED(device) {}
3121
Florin Malitaee424ac2016-12-01 12:47:59 -05003122SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3123 (void)this->INHERITED::getSaveLayerStrategy(rec);
3124 return kNoLayer_SaveLayerStrategy;
3125}
3126
Mike Reed148b7fd2018-12-18 17:38:18 -05003127bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3128 return false;
3129}
3130
Florin Malitaee424ac2016-12-01 12:47:59 -05003131///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003132
reed73603f32016-09-20 08:42:38 -07003133static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3134static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3135static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3136static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3137static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3138static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003139
3140///////////////////////////////////////////////////////////////////////////////////////////////////
3141
3142SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3143 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003144 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003145 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwig915b7792019-10-22 17:40:41 +00003146 SkIPoint origin = dev->getOrigin();
3147 SkMatrix ctm = this->getTotalMatrix();
3148 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3149
3150 SkIRect clip = fMCRec->fRasterClip.getBounds();
3151 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003152 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003153 clip.setEmpty();
3154 }
3155
Michael Ludwig915b7792019-10-22 17:40:41 +00003156 fAllocator->updateHandle(handle, ctm, clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003157 return handle;
3158 }
3159 return nullptr;
3160}
3161
3162static bool install(SkBitmap* bm, const SkImageInfo& info,
3163 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003164 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003165}
3166
3167SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3168 SkBitmap* bm) {
3169 SkRasterHandleAllocator::Rec rec;
3170 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3171 return nullptr;
3172 }
3173 return rec.fHandle;
3174}
3175
3176std::unique_ptr<SkCanvas>
3177SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3178 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003179 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003180 return nullptr;
3181 }
3182
3183 SkBitmap bm;
3184 Handle hndl;
3185
3186 if (rec) {
3187 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3188 } else {
3189 hndl = alloc->allocBitmap(info, &bm);
3190 }
3191 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3192}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003193
3194///////////////////////////////////////////////////////////////////////////////////////////////////