blob: 08e1b84e5061f2a360a1e08799f884ac523a6082 [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
Brian Osman11052242016-10-27 14:47:55 -04001800GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001801 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001802 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001803}
1804
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001805GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001806 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001807 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001808}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001809
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001810void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1811 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001812 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001813 if (outer.isEmpty()) {
1814 return;
1815 }
1816 if (inner.isEmpty()) {
1817 this->drawRRect(outer, paint);
1818 return;
1819 }
1820
1821 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001822 // be able to return ...
1823 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001824 //
1825 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001826 if (!outer.getBounds().contains(inner.getBounds())) {
1827 return;
1828 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001829
1830 this->onDrawDRRect(outer, inner, paint);
1831}
1832
reed41af9662015-01-05 07:49:08 -08001833void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001834 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001835 this->onDrawPaint(paint);
1836}
1837
1838void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001839 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001840 // To avoid redundant logic in our culling code and various backends, we always sort rects
1841 // before passing them along.
1842 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001843}
1844
Mike Reedd5674082019-04-19 15:00:47 -04001845void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1846 TRACE_EVENT0("skia", TRACE_FUNC);
1847 this->onDrawBehind(paint);
1848}
1849
msarettdca352e2016-08-26 06:37:45 -07001850void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001851 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001852 if (region.isEmpty()) {
1853 return;
1854 }
1855
1856 if (region.isRect()) {
1857 return this->drawIRect(region.getBounds(), paint);
1858 }
1859
1860 this->onDrawRegion(region, paint);
1861}
1862
reed41af9662015-01-05 07:49:08 -08001863void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001864 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001865 // To avoid redundant logic in our culling code and various backends, we always sort rects
1866 // before passing them along.
1867 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001868}
1869
1870void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001871 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001872 this->onDrawRRect(rrect, paint);
1873}
1874
1875void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001876 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001877 this->onDrawPoints(mode, count, pts, paint);
1878}
1879
Mike Reede88a1cb2017-03-17 09:50:46 -04001880void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1881 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001882 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001883 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001884 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1885 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001886 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001887}
1888
1889void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001890 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001891 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001892 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1893}
1894
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001895void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1896 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001897 TRACE_EVENT0("skia", TRACE_FUNC);
1898 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001899 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001900 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1901}
1902
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001903void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1904 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001905 TRACE_EVENT0("skia", TRACE_FUNC);
1906 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001907 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001908 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001909}
1910
1911void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001912 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001913 this->onDrawPath(path, paint);
1914}
1915
reeda85d4d02015-05-06 12:56:48 -07001916void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001917 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001918 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001919 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001920}
1921
Mike Reedc4e31092018-01-30 11:15:27 -05001922// Returns true if the rect can be "filled" : non-empty and finite
1923static bool fillable(const SkRect& r) {
1924 SkScalar w = r.width();
1925 SkScalar h = r.height();
1926 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1927}
1928
reede47829b2015-08-06 10:02:53 -07001929void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1930 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001931 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001932 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001933 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001934 return;
1935 }
1936 this->onDrawImageRect(image, &src, dst, paint, constraint);
1937}
reed41af9662015-01-05 07:49:08 -08001938
reed84984ef2015-07-17 07:09:43 -07001939void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1940 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001941 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001942 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001943}
1944
Brian Salomonf08002c2018-10-26 16:15:46 -04001945void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001946 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001947 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001948 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001949}
reede47829b2015-08-06 10:02:53 -07001950
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001951namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001952class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001953public:
Brian Salomon969be1c2018-05-21 14:37:49 -04001954 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
1955 if (!origPaint) {
1956 return;
1957 }
1958 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
1959 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
1960 }
1961 if (origPaint->getMaskFilter()) {
1962 fPaint.writable()->setMaskFilter(nullptr);
1963 }
1964 if (origPaint->isAntiAlias()) {
1965 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001966 }
1967 }
1968
1969 const SkPaint* get() const {
1970 return fPaint;
1971 }
1972
1973private:
Brian Salomon969be1c2018-05-21 14:37:49 -04001974 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001975};
1976} // namespace
1977
reed4c21dc52015-06-25 12:32:03 -07001978void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1979 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001980 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001981 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001982 if (dst.isEmpty()) {
1983 return;
1984 }
msarett552bca92016-08-03 06:53:26 -07001985 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001986 LatticePaint latticePaint(paint);
1987 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07001988 } else {
reede47829b2015-08-06 10:02:53 -07001989 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001990 }
reed4c21dc52015-06-25 12:32:03 -07001991}
1992
msarett16882062016-08-16 09:31:08 -07001993void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1994 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001995 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07001996 RETURN_ON_NULL(image);
1997 if (dst.isEmpty()) {
1998 return;
1999 }
msarett71df2d72016-09-30 12:41:42 -07002000
2001 SkIRect bounds;
2002 Lattice latticePlusBounds = lattice;
2003 if (!latticePlusBounds.fBounds) {
2004 bounds = SkIRect::MakeWH(image->width(), image->height());
2005 latticePlusBounds.fBounds = &bounds;
2006 }
2007
2008 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002009 LatticePaint latticePaint(paint);
2010 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002011 } else {
2012 this->drawImageRect(image, dst, paint);
2013 }
2014}
2015
reed41af9662015-01-05 07:49:08 -08002016void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002017 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002018 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002019 return;
2020 }
reed41af9662015-01-05 07:49:08 -08002021 this->onDrawBitmap(bitmap, dx, dy, paint);
2022}
2023
reede47829b2015-08-06 10:02:53 -07002024void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002025 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002026 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002027 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002028 return;
2029 }
reede47829b2015-08-06 10:02:53 -07002030 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002031}
2032
reed84984ef2015-07-17 07:09:43 -07002033void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2034 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002035 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002036}
2037
reede47829b2015-08-06 10:02:53 -07002038void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2039 SrcRectConstraint constraint) {
2040 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2041 constraint);
2042}
reede47829b2015-08-06 10:02:53 -07002043
reed41af9662015-01-05 07:49:08 -08002044void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2045 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002046 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002047 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002048 return;
2049 }
msarett552bca92016-08-03 06:53:26 -07002050 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002051 LatticePaint latticePaint(paint);
2052 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002053 } else {
reeda5517e22015-07-14 10:54:12 -07002054 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002055 }
reed41af9662015-01-05 07:49:08 -08002056}
2057
msarettc573a402016-08-02 08:05:56 -07002058void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2059 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002060 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002061 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002062 return;
2063 }
msarett71df2d72016-09-30 12:41:42 -07002064
2065 SkIRect bounds;
2066 Lattice latticePlusBounds = lattice;
2067 if (!latticePlusBounds.fBounds) {
2068 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2069 latticePlusBounds.fBounds = &bounds;
2070 }
2071
2072 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002073 LatticePaint latticePaint(paint);
2074 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002075 } else {
msarett16882062016-08-16 09:31:08 -07002076 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002077 }
msarettc573a402016-08-02 08:05:56 -07002078}
2079
reed71c3c762015-06-24 10:29:17 -07002080void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002081 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002082 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002083 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002084 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002085 if (count <= 0) {
2086 return;
2087 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002088 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002089 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002090 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002091}
2092
reedf70b5312016-03-04 16:36:20 -08002093void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002094 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002095 if (key) {
2096 this->onDrawAnnotation(rect, key, value);
2097 }
2098}
2099
reede47829b2015-08-06 10:02:53 -07002100void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2101 const SkPaint* paint, SrcRectConstraint constraint) {
2102 if (src) {
2103 this->drawImageRect(image, *src, dst, paint, constraint);
2104 } else {
2105 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2106 dst, paint, constraint);
2107 }
2108}
2109void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2110 const SkPaint* paint, SrcRectConstraint constraint) {
2111 if (src) {
2112 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2113 } else {
2114 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2115 dst, paint, constraint);
2116 }
2117}
2118
Mike Reed4204da22017-05-17 08:53:36 -04002119void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002120 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002121 this->onDrawShadowRec(path, rec);
2122}
2123
2124void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2125 SkPaint paint;
2126 const SkRect& pathBounds = path.getBounds();
2127
Mike Reed38992392019-07-30 10:48:15 -04002128 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002129 while (iter.next()) {
2130 iter.fDevice->drawShadow(path, rec);
2131 }
Mike Reed38992392019-07-30 10:48:15 -04002132 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002133}
2134
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002135void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002136 QuadAAFlags aaFlags, const SkColor4f& color,
2137 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002138 TRACE_EVENT0("skia", TRACE_FUNC);
2139 // Make sure the rect is sorted before passing it along
2140 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2141}
2142
2143void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2144 const SkPoint dstClips[],
2145 const SkMatrix preViewMatrices[],
2146 const SkPaint* paint,
2147 SrcRectConstraint constraint) {
2148 TRACE_EVENT0("skia", TRACE_FUNC);
2149 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2150}
2151
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152//////////////////////////////////////////////////////////////////////////////
2153// These are the virtual drawing methods
2154//////////////////////////////////////////////////////////////////////////////
2155
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002156void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002157 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002158 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2159 }
2160}
2161
reed41af9662015-01-05 07:49:08 -08002162void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002163 this->internalDrawPaint(paint);
2164}
2165
2166void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002167 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002168
2169 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002170 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171 }
2172
Mike Reed38992392019-07-30 10:48:15 -04002173 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174}
2175
reed41af9662015-01-05 07:49:08 -08002176void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2177 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178 if ((long)count <= 0) {
2179 return;
2180 }
2181
Mike Reed822128b2017-02-28 16:41:03 -05002182 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002183 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002184 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002185 // special-case 2 points (common for drawing a single line)
2186 if (2 == count) {
2187 r.set(pts[0], pts[1]);
2188 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002189 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002190 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002191 if (!r.isFinite()) {
2192 return;
2193 }
Mike Reed822128b2017-02-28 16:41:03 -05002194 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002195 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2196 return;
2197 }
2198 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002199 }
reed@google.coma584aed2012-05-16 14:06:02 +00002200
halcanary96fcdcc2015-08-27 07:41:13 -07002201 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202
Mike Reed38992392019-07-30 10:48:15 -04002203 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002204
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002206 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 }
reed@google.com4b226022011-01-11 18:32:13 +00002208
Mike Reed38992392019-07-30 10:48:15 -04002209 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210}
2211
reed4a167172016-08-18 17:15:25 -07002212static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002213 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002214}
2215
reed41af9662015-01-05 07:49:08 -08002216void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002217 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002219 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002220 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002221 return;
2222 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002223 }
reed@google.com4b226022011-01-11 18:32:13 +00002224
reed4a167172016-08-18 17:15:25 -07002225 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002226 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227
reed4a167172016-08-18 17:15:25 -07002228 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002229 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002230 }
2231
Mike Reed38992392019-07-30 10:48:15 -04002232 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002233 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002234 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002235 SkDrawIter iter(this);
2236 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002237 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002238 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240}
2241
msarett44df6512016-08-25 13:54:30 -07002242void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002243 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002244 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002245 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002246 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2247 return;
2248 }
msarett44df6512016-08-25 13:54:30 -07002249 }
2250
Mike Reed38992392019-07-30 10:48:15 -04002251 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002252
2253 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002254 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002255 }
2256
Mike Reed38992392019-07-30 10:48:15 -04002257 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002258}
2259
Mike Reedd5674082019-04-19 15:00:47 -04002260void SkCanvas::onDrawBehind(const SkPaint& paint) {
2261 SkIRect bounds;
2262 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2263 for (;;) {
2264 const MCRec* rec = (const MCRec*)iter.prev();
2265 if (!rec) {
2266 return; // no backimages, so nothing to draw
2267 }
2268 if (rec->fBackImage) {
2269 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2270 rec->fBackImage->fImage->width(),
2271 rec->fBackImage->fImage->height());
2272 break;
2273 }
2274 }
2275
Mike Reed38992392019-07-30 10:48:15 -04002276 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002277
2278 while (iter.next()) {
2279 SkBaseDevice* dev = iter.fDevice;
2280
Mike Reedd5674082019-04-19 15:00:47 -04002281 dev->save();
2282 // We use clipRegion because it is already defined to operate in dev-space
2283 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2284 // but we don't want that, so we undo that before calling in.
Michael Ludwig915b7792019-10-22 17:40:41 +00002285 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002286 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002287 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002288 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002289 }
2290
Mike Reed38992392019-07-30 10:48:15 -04002291 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002292}
2293
reed41af9662015-01-05 07:49:08 -08002294void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002295 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002296 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002297 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002298 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002299 return;
2300 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002301 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002302
Mike Reed38992392019-07-30 10:48:15 -04002303 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002304
2305 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002306 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002307 }
2308
Mike Reed38992392019-07-30 10:48:15 -04002309 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002310}
2311
bsalomonac3aa242016-08-19 11:25:19 -07002312void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2313 SkScalar sweepAngle, bool useCenter,
2314 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002315 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002316 if (paint.canComputeFastBounds()) {
2317 SkRect storage;
2318 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002319 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002320 return;
2321 }
bsalomonac3aa242016-08-19 11:25:19 -07002322 }
2323
Mike Reed38992392019-07-30 10:48:15 -04002324 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002325
2326 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002327 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002328 }
2329
Mike Reed38992392019-07-30 10:48:15 -04002330 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002331}
2332
reed41af9662015-01-05 07:49:08 -08002333void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002334 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002335 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002336 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2337 return;
2338 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002339 }
2340
2341 if (rrect.isRect()) {
2342 // call the non-virtual version
2343 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002344 return;
2345 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002346 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002347 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2348 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002349 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002350
Mike Reed38992392019-07-30 10:48:15 -04002351 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002352
2353 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002354 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002355 }
2356
Mike Reed38992392019-07-30 10:48:15 -04002357 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002358}
2359
Mike Reed822128b2017-02-28 16:41:03 -05002360void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002361 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002362 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002363 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2364 return;
2365 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002366 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002367
Mike Reed38992392019-07-30 10:48:15 -04002368 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002369
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002370 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002371 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002372 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002373
Mike Reed38992392019-07-30 10:48:15 -04002374 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002375}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002376
reed41af9662015-01-05 07:49:08 -08002377void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002378 if (!path.isFinite()) {
2379 return;
2380 }
2381
Mike Reed822128b2017-02-28 16:41:03 -05002382 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002383 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002384 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002385 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2386 return;
2387 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002389
Mike Reed822128b2017-02-28 16:41:03 -05002390 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002391 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002392 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002393 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002394 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002395 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002396
Mike Reed38992392019-07-30 10:48:15 -04002397 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398
2399 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002400 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 }
2402
Mike Reed38992392019-07-30 10:48:15 -04002403 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002404}
2405
reed262a71b2015-12-05 13:07:27 -08002406bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002407 if (!paint.getImageFilter()) {
2408 return false;
2409 }
2410
2411 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002412 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002413 return false;
2414 }
2415
2416 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2417 // Once we can filter and the filter will return a result larger than itself, we should be
2418 // able to remove this constraint.
2419 // skbug.com/4526
2420 //
2421 SkPoint pt;
2422 ctm.mapXY(x, y, &pt);
2423 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2424 return ir.contains(fMCRec->fRasterClip.getBounds());
2425}
2426
Mike Reedf441cfc2018-04-11 14:50:16 -04002427// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2428// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2429// null.
2430static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2431 if (paintParam) {
2432 *real = *paintParam;
2433 real->setStyle(SkPaint::kFill_Style);
2434 real->setPathEffect(nullptr);
2435 paintParam = real;
2436 }
2437 return paintParam;
2438}
2439
reeda85d4d02015-05-06 12:56:48 -07002440void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002441 SkPaint realPaint;
2442 paint = init_image_paint(&realPaint, paint);
2443
reeda85d4d02015-05-06 12:56:48 -07002444 SkRect bounds = SkRect::MakeXYWH(x, y,
2445 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002446 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002447 SkRect tmp = bounds;
2448 if (paint) {
2449 paint->computeFastBounds(tmp, &tmp);
2450 }
2451 if (this->quickReject(tmp)) {
2452 return;
2453 }
reeda85d4d02015-05-06 12:56:48 -07002454 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002455 // At this point we need a real paint object. If the caller passed null, then we should
2456 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2457 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2458 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002459
reeda2217ef2016-07-20 06:04:34 -07002460 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002461 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2462 *paint);
2463 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002464 special = this->getDevice()->makeSpecial(image);
2465 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002466 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002467 }
2468 }
2469
Mike Reed38992392019-07-30 10:48:15 -04002470 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002471
reeda85d4d02015-05-06 12:56:48 -07002472 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002473 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002474 if (special) {
2475 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002476 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002477 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002478 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002479 SkScalarRoundToInt(pt.fY), pnt,
2480 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002481 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002482 iter.fDevice->drawImageRect(
2483 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2484 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002485 }
reeda85d4d02015-05-06 12:56:48 -07002486 }
halcanary9d524f22016-03-29 09:03:52 -07002487
Mike Reed38992392019-07-30 10:48:15 -04002488 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002489}
2490
reed41af9662015-01-05 07:49:08 -08002491void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002492 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002493 SkPaint realPaint;
2494 paint = init_image_paint(&realPaint, paint);
2495
halcanary96fcdcc2015-08-27 07:41:13 -07002496 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002497 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002498 if (paint) {
2499 paint->computeFastBounds(dst, &storage);
2500 }
2501 if (this->quickReject(storage)) {
2502 return;
2503 }
reeda85d4d02015-05-06 12:56:48 -07002504 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002505 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002506
Mike Reed38992392019-07-30 10:48:15 -04002507 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002508
reeda85d4d02015-05-06 12:56:48 -07002509 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002510 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002511 }
halcanary9d524f22016-03-29 09:03:52 -07002512
Mike Reed38992392019-07-30 10:48:15 -04002513 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002514}
2515
reed41af9662015-01-05 07:49:08 -08002516void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002517 SkDEBUGCODE(bitmap.validate();)
2518
reed33366972015-10-08 09:22:02 -07002519 if (bitmap.drawsNothing()) {
2520 return;
2521 }
2522
Mike Reedf441cfc2018-04-11 14:50:16 -04002523 SkPaint realPaint;
2524 init_image_paint(&realPaint, paint);
2525 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002526
Mike Reed822128b2017-02-28 16:41:03 -05002527 SkRect bounds;
2528 bitmap.getBounds(&bounds);
2529 bounds.offset(x, y);
2530 bool canFastBounds = paint->canComputeFastBounds();
2531 if (canFastBounds) {
2532 SkRect storage;
2533 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002534 return;
2535 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002536 }
reed@google.com4b226022011-01-11 18:32:13 +00002537
reeda2217ef2016-07-20 06:04:34 -07002538 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002539 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2540 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002541 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002542 special = this->getDevice()->makeSpecial(bitmap);
2543 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002544 drawAsSprite = false;
2545 }
2546 }
2547
Mike Reed38992392019-07-30 10:48:15 -04002548 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002549
2550 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002551 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002552 if (special) {
reed262a71b2015-12-05 13:07:27 -08002553 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002554 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002555 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002556 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002557 SkScalarRoundToInt(pt.fY), pnt,
2558 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002559 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002560 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2561 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2562 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002563 }
reed33366972015-10-08 09:22:02 -07002564 }
msarettfbfa2582016-08-12 08:29:08 -07002565
Mike Reed38992392019-07-30 10:48:15 -04002566 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002567}
2568
reed@google.com9987ec32011-09-07 11:57:52 +00002569// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002570void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002571 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002572 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002573 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002574 return;
2575 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002576
halcanary96fcdcc2015-08-27 07:41:13 -07002577 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002578 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002579 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2580 return;
2581 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002582 }
reed@google.com3d608122011-11-21 15:16:16 +00002583
reed@google.com33535f32012-09-25 15:37:50 +00002584 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002585 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002586 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002587 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002588
Mike Reed38992392019-07-30 10:48:15 -04002589 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002590
reed@google.com33535f32012-09-25 15:37:50 +00002591 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002592 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002593 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002594
Mike Reed38992392019-07-30 10:48:15 -04002595 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002596}
2597
reed41af9662015-01-05 07:49:08 -08002598void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002599 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002600 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002601 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002602}
2603
reed4c21dc52015-06-25 12:32:03 -07002604void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2605 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002606 SkPaint realPaint;
2607 paint = init_image_paint(&realPaint, paint);
2608
halcanary96fcdcc2015-08-27 07:41:13 -07002609 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002610 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002611 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2612 return;
2613 }
reed@google.com3d608122011-11-21 15:16:16 +00002614 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002615 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002616
Mike Reed38992392019-07-30 10:48:15 -04002617 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002618
reed4c21dc52015-06-25 12:32:03 -07002619 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002620 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002621 }
halcanary9d524f22016-03-29 09:03:52 -07002622
Mike Reed38992392019-07-30 10:48:15 -04002623 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002624}
2625
reed41af9662015-01-05 07:49:08 -08002626void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2627 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002628 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002629 SkPaint realPaint;
2630 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002631
halcanary96fcdcc2015-08-27 07:41:13 -07002632 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002633 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002634 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2635 return;
2636 }
reed4c21dc52015-06-25 12:32:03 -07002637 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002638 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002639
Mike Reed38992392019-07-30 10:48:15 -04002640 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002641
reed4c21dc52015-06-25 12:32:03 -07002642 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002643 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002644 }
halcanary9d524f22016-03-29 09:03:52 -07002645
Mike Reed38992392019-07-30 10:48:15 -04002646 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002647}
2648
msarett16882062016-08-16 09:31:08 -07002649void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2650 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002651 SkPaint realPaint;
2652 paint = init_image_paint(&realPaint, paint);
2653
msarett16882062016-08-16 09:31:08 -07002654 if (nullptr == paint || paint->canComputeFastBounds()) {
2655 SkRect storage;
2656 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2657 return;
2658 }
2659 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002660 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002661
Mike Reed38992392019-07-30 10:48:15 -04002662 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002663
2664 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002665 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002666 }
2667
Mike Reed38992392019-07-30 10:48:15 -04002668 DRAW_END
msarett16882062016-08-16 09:31:08 -07002669}
2670
2671void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2672 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002673 SkPaint realPaint;
2674 paint = init_image_paint(&realPaint, paint);
2675
msarett16882062016-08-16 09:31:08 -07002676 if (nullptr == paint || paint->canComputeFastBounds()) {
2677 SkRect storage;
2678 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2679 return;
2680 }
2681 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002682 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002683
Mike Reed38992392019-07-30 10:48:15 -04002684 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002685
2686 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002687 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002688 }
2689
Mike Reed38992392019-07-30 10:48:15 -04002690 DRAW_END
msarett16882062016-08-16 09:31:08 -07002691}
2692
fmalita00d5c2c2014-08-21 08:53:26 -07002693void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2694 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002695 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002696 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002697 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002698 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002699 SkRect tmp;
2700 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2701 return;
2702 }
2703 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002704 }
2705
fmalita024f9962015-03-03 19:08:17 -08002706 // We cannot filter in the looper as we normally do, because the paint is
2707 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002708 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002709
fmalitaaa1b9122014-08-28 14:32:24 -07002710 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002711 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002712 }
2713
Mike Reed38992392019-07-30 10:48:15 -04002714 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002715}
2716
Mike Reed358fcad2018-11-23 15:27:51 -05002717// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002718void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002719 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2720 TRACE_EVENT0("skia", TRACE_FUNC);
2721 if (byteLength) {
2722 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002723 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002724 }
2725}
Mike Reed4f81bb72019-01-23 09:23:00 -05002726
fmalita00d5c2c2014-08-21 08:53:26 -07002727void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2728 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002729 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002730 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002731 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002732 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002733}
reed@google.come0d9ce82014-04-23 04:00:17 +00002734
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002735void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002736 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002737 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002738
2739 while (iter.next()) {
2740 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002741 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002742 }
2743
Mike Reed38992392019-07-30 10:48:15 -04002744 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002745}
2746
dandovb3c9d1c2014-08-12 08:34:29 -07002747void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002748 const SkPoint texCoords[4], SkBlendMode bmode,
2749 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002750 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002751 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002752 return;
2753 }
mtklein6cfa73a2014-08-13 13:33:49 -07002754
Mike Reedfaba3712016-11-03 14:45:31 -04002755 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002756}
2757
2758void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002759 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002760 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002761 // Since a patch is always within the convex hull of the control points, we discard it when its
2762 // bounding rectangle is completely outside the current clip.
2763 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002764 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002765 if (this->quickReject(bounds)) {
2766 return;
2767 }
mtklein6cfa73a2014-08-13 13:33:49 -07002768
Mike Reed38992392019-07-30 10:48:15 -04002769 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002770
dandovecfff212014-08-04 10:02:00 -07002771 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002772 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002773 }
mtklein6cfa73a2014-08-13 13:33:49 -07002774
Mike Reed38992392019-07-30 10:48:15 -04002775 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002776}
2777
reeda8db7282015-07-07 10:22:31 -07002778void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002779#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002780 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002781#endif
reede3b38ce2016-01-08 09:18:44 -08002782 RETURN_ON_NULL(dr);
2783 if (x || y) {
2784 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2785 this->onDrawDrawable(dr, &matrix);
2786 } else {
2787 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002788 }
2789}
2790
reeda8db7282015-07-07 10:22:31 -07002791void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002792#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002793 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002794#endif
reede3b38ce2016-01-08 09:18:44 -08002795 RETURN_ON_NULL(dr);
2796 if (matrix && matrix->isIdentity()) {
2797 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002798 }
reede3b38ce2016-01-08 09:18:44 -08002799 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002800}
2801
2802void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002803 // drawable bounds are no longer reliable (e.g. android displaylist)
2804 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002805 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002806}
2807
reed71c3c762015-06-24 10:29:17 -07002808void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002809 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002810 const SkRect* cull, const SkPaint* paint) {
2811 if (cull && this->quickReject(*cull)) {
2812 return;
2813 }
2814
2815 SkPaint pnt;
2816 if (paint) {
2817 pnt = *paint;
2818 }
halcanary9d524f22016-03-29 09:03:52 -07002819
Mike Reed38992392019-07-30 10:48:15 -04002820 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002821 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002822 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002823 }
Mike Reed38992392019-07-30 10:48:15 -04002824 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002825}
2826
reedf70b5312016-03-04 16:36:20 -08002827void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2828 SkASSERT(key);
2829
2830 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002831 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002832 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002833 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002834 }
Mike Reed38992392019-07-30 10:48:15 -04002835 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002836}
2837
Michael Ludwiga595f862019-08-27 15:25:49 -04002838void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2839 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002840 SkASSERT(r.isSorted());
2841
2842 // If this used a paint, it would be a filled color with blend mode, which does not
2843 // need to use an autodraw loop, so use SkDrawIter directly.
2844 if (this->quickReject(r)) {
2845 return;
2846 }
2847
Michael Ludwiga4b44882019-08-28 14:34:58 -04002848 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002849 SkDrawIter iter(this);
2850 while(iter.next()) {
2851 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2852 }
2853}
2854
2855void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2856 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2857 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002858 if (count <= 0) {
2859 // Nothing to draw
2860 return;
2861 }
2862
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002863 SkPaint realPaint;
2864 init_image_paint(&realPaint, paint);
2865
Michael Ludwiga4b44882019-08-28 14:34:58 -04002866 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2867 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2868 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2869 // or we need it for the autolooper (since it greatly improves image filter perf).
2870 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2871 bool setBoundsValid = count == 1 || needsAutoLooper;
2872 SkRect setBounds = imageSet[0].fDstRect;
2873 if (imageSet[0].fMatrixIndex >= 0) {
2874 // Account for the per-entry transform that is applied prior to the CTM when drawing
2875 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002876 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002877 if (needsAutoLooper) {
2878 for (int i = 1; i < count; ++i) {
2879 SkRect entryBounds = imageSet[i].fDstRect;
2880 if (imageSet[i].fMatrixIndex >= 0) {
2881 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2882 }
2883 setBounds.joinPossiblyEmptyRect(entryBounds);
2884 }
2885 }
2886
2887 // If we happen to have the draw bounds, though, might as well check quickReject().
2888 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2889 SkRect tmp;
2890 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2891 return;
2892 }
2893 }
2894
2895 if (needsAutoLooper) {
2896 SkASSERT(setBoundsValid);
2897 DRAW_BEGIN(realPaint, &setBounds)
2898 while (iter.next()) {
2899 iter.fDevice->drawEdgeAAImageSet(
2900 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2901 }
2902 DRAW_END
2903 } else {
2904 this->predrawNotify();
2905 SkDrawIter iter(this);
2906 while(iter.next()) {
2907 iter.fDevice->drawEdgeAAImageSet(
2908 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2909 }
2910 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002911}
2912
reed@android.com8a1c16f2008-12-17 15:59:43 +00002913//////////////////////////////////////////////////////////////////////////////
2914// These methods are NOT virtual, and therefore must call back into virtual
2915// methods, rather than actually drawing themselves.
2916//////////////////////////////////////////////////////////////////////////////
2917
reed374772b2016-10-05 17:33:02 -07002918void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002919 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002921 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002922 this->drawPaint(paint);
2923}
2924
2925void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002926 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2928}
2929
Mike Reed3661bc92017-02-22 13:21:42 -05002930void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002931 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002932 pts[0].set(x0, y0);
2933 pts[1].set(x1, y1);
2934 this->drawPoints(kLines_PointMode, 2, pts, paint);
2935}
2936
Mike Reed3661bc92017-02-22 13:21:42 -05002937void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002938 if (radius < 0) {
2939 radius = 0;
2940 }
2941
2942 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002943 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002944 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002945}
2946
2947void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2948 const SkPaint& paint) {
2949 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002950 SkRRect rrect;
2951 rrect.setRectXY(r, rx, ry);
2952 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953 } else {
2954 this->drawRect(r, paint);
2955 }
2956}
2957
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2959 SkScalar sweepAngle, bool useCenter,
2960 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002961 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002962 if (oval.isEmpty() || !sweepAngle) {
2963 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002964 }
bsalomon21af9ca2016-08-25 12:29:23 -07002965 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002966}
2967
reed@android.comf76bacf2009-05-13 14:00:33 +00002968///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002969#ifdef SK_DISABLE_SKPICTURE
2970void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002971
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002972
2973void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2974 const SkPaint* paint) {}
2975#else
Mike Klein88d90712018-01-27 17:30:04 +00002976/**
2977 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2978 * against the playback cost of recursing into the subpicture to get at its actual ops.
2979 *
2980 * For now we pick a conservatively small value, though measurement (and other heuristics like
2981 * the type of ops contained) may justify changing this value.
2982 */
2983#define kMaxPictureOpsToUnrollInsteadOfRef 1
2984
reedd5fa1a42014-08-09 11:08:05 -07002985void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002986 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002987 RETURN_ON_NULL(picture);
2988
reede3b38ce2016-01-08 09:18:44 -08002989 if (matrix && matrix->isIdentity()) {
2990 matrix = nullptr;
2991 }
Mike Klein88d90712018-01-27 17:30:04 +00002992 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2993 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2994 picture->playback(this);
2995 } else {
2996 this->onDrawPicture(picture, matrix, paint);
2997 }
reedd5fa1a42014-08-09 11:08:05 -07002998}
robertphillips9b14f262014-06-04 05:40:44 -07002999
reedd5fa1a42014-08-09 11:08:05 -07003000void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3001 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003002 if (!paint || paint->canComputeFastBounds()) {
3003 SkRect bounds = picture->cullRect();
3004 if (paint) {
3005 paint->computeFastBounds(bounds, &bounds);
3006 }
3007 if (matrix) {
3008 matrix->mapRect(&bounds);
3009 }
3010 if (this->quickReject(bounds)) {
3011 return;
3012 }
3013 }
3014
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003015 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003016 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003018#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003019
reed@android.com8a1c16f2008-12-17 15:59:43 +00003020///////////////////////////////////////////////////////////////////////////////
3021///////////////////////////////////////////////////////////////////////////////
3022
reed3aafe112016-08-18 12:45:34 -07003023SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003024 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003025
3026 SkASSERT(canvas);
3027
reed3aafe112016-08-18 12:45:34 -07003028 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003029 fDone = !fImpl->next();
3030}
3031
3032SkCanvas::LayerIter::~LayerIter() {
3033 fImpl->~SkDrawIter();
3034}
3035
3036void SkCanvas::LayerIter::next() {
3037 fDone = !fImpl->next();
3038}
3039
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003040SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003041 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003042}
3043
3044const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003045 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003046}
3047
3048const SkPaint& SkCanvas::LayerIter::paint() const {
3049 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003050 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003051 paint = &fDefaultPaint;
3052 }
3053 return *paint;
3054}
3055
Mike Reedca37f322018-03-08 13:22:16 -05003056SkIRect SkCanvas::LayerIter::clipBounds() const {
3057 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003058}
3059
Michael Ludwig915b7792019-10-22 17:40:41 +00003060int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3061int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003062
3063///////////////////////////////////////////////////////////////////////////////
3064
Brian Osmane8a98632019-04-10 10:26:10 -04003065SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3066SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3067SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3068SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3069
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003070SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3071 const SkRect& dstRect, int matrixIndex, float alpha,
3072 unsigned aaFlags, bool hasClip)
3073 : fImage(std::move(image))
3074 , fSrcRect(srcRect)
3075 , fDstRect(dstRect)
3076 , fMatrixIndex(matrixIndex)
3077 , fAlpha(alpha)
3078 , fAAFlags(aaFlags)
3079 , fHasClip(hasClip) {}
3080
3081SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3082 const SkRect& dstRect, float alpha, unsigned aaFlags)
3083 : fImage(std::move(image))
3084 , fSrcRect(srcRect)
3085 , fDstRect(dstRect)
3086 , fAlpha(alpha)
3087 , fAAFlags(aaFlags) {}
3088
3089///////////////////////////////////////////////////////////////////////////////
3090
Mike Reed5df49342016-11-12 08:06:55 -06003091std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003092 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003093 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003094 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003095 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003096
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003097 SkBitmap bitmap;
3098 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003099 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003100 }
Mike Reed12f77342017-11-08 11:19:52 -05003101
3102 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003103 std::make_unique<SkCanvas>(bitmap, *props) :
3104 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003105}
reedd5fa1a42014-08-09 11:08:05 -07003106
3107///////////////////////////////////////////////////////////////////////////////
3108
Florin Malitaee424ac2016-12-01 12:47:59 -05003109SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003110 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003111
Florin Malita439ace92016-12-02 12:05:41 -05003112SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003113 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003114
Herb Derbyefe39bc2018-05-01 17:06:20 -04003115SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003116 : INHERITED(device) {}
3117
Florin Malitaee424ac2016-12-01 12:47:59 -05003118SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3119 (void)this->INHERITED::getSaveLayerStrategy(rec);
3120 return kNoLayer_SaveLayerStrategy;
3121}
3122
Mike Reed148b7fd2018-12-18 17:38:18 -05003123bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3124 return false;
3125}
3126
Florin Malitaee424ac2016-12-01 12:47:59 -05003127///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003128
reed73603f32016-09-20 08:42:38 -07003129static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3130static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3131static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3132static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3133static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3134static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003135
3136///////////////////////////////////////////////////////////////////////////////////////////////////
3137
3138SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3139 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003140 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003141 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwig915b7792019-10-22 17:40:41 +00003142 SkIPoint origin = dev->getOrigin();
3143 SkMatrix ctm = this->getTotalMatrix();
3144 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3145
3146 SkIRect clip = fMCRec->fRasterClip.getBounds();
3147 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003148 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003149 clip.setEmpty();
3150 }
3151
Michael Ludwig915b7792019-10-22 17:40:41 +00003152 fAllocator->updateHandle(handle, ctm, clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003153 return handle;
3154 }
3155 return nullptr;
3156}
3157
3158static bool install(SkBitmap* bm, const SkImageInfo& info,
3159 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003160 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003161}
3162
3163SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3164 SkBitmap* bm) {
3165 SkRasterHandleAllocator::Rec rec;
3166 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3167 return nullptr;
3168 }
3169 return rec.fHandle;
3170}
3171
3172std::unique_ptr<SkCanvas>
3173SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3174 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003175 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003176 return nullptr;
3177 }
3178
3179 SkBitmap bm;
3180 Handle hndl;
3181
3182 if (rec) {
3183 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3184 } else {
3185 hndl = alloc->allocBitmap(info, &bm);
3186 }
3187 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3188}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003189
3190///////////////////////////////////////////////////////////////////////////////////////////////////