blob: cc4627757ad44b3e9d10f6147f5e0b4ed3f5f36a [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/private/SkNx.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040023#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050025#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/core/SkCanvasPriv.h"
27#include "src/core/SkClipOpPriv.h"
28#include "src/core/SkClipStack.h"
29#include "src/core/SkDraw.h"
30#include "src/core/SkGlyphRun.h"
31#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040032#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkLatticeIter.h"
34#include "src/core/SkMSAN.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050035#include "src/core/SkMatrixUtils.h"
36#include "src/core/SkPaintPriv.h"
37#include "src/core/SkRasterClip.h"
38#include "src/core/SkSpecialImage.h"
39#include "src/core/SkStrikeCache.h"
40#include "src/core/SkTLazy.h"
41#include "src/core/SkTextFormatParams.h"
42#include "src/core/SkTraceEvent.h"
43#include "src/image/SkImage_Base.h"
44#include "src/image/SkSurface_Base.h"
45#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040046
bungemand3ebb482015-08-05 13:57:49 -070047#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000048
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000049#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050050#include "include/gpu/GrContext.h"
51#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#endif
53
reede3b38ce2016-01-08 09:18:44 -080054#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050055#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080056
Mike Klein1bb7e232019-12-10 08:58:52 -060057// This is a test: static_assert with no message is a c++17 feature,
58// and std::max() is constexpr only since the c++14 stdlib.
59static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000060
Mike Reed139e5e02017-03-08 11:29:33 -050061///////////////////////////////////////////////////////////////////////////////////////////////////
62
reedc83a2972015-07-16 07:40:45 -070063/*
64 * Return true if the drawing this rect would hit every pixels in the canvas.
65 *
66 * Returns false if
67 * - rect does not contain the canvas' bounds
68 * - paint is not fill
69 * - paint would blur or otherwise change the coverage of the rect
70 */
71bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
72 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070073 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
74 (int)kNone_ShaderOverrideOpacity,
75 "need_matching_enums0");
76 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
77 (int)kOpaque_ShaderOverrideOpacity,
78 "need_matching_enums1");
79 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
80 (int)kNotOpaque_ShaderOverrideOpacity,
81 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070082
83 const SkISize size = this->getBaseLayerSize();
84 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050085
86 // if we're clipped at all, we can't overwrite the entire surface
87 {
88 SkBaseDevice* base = this->getDevice();
89 SkBaseDevice* top = this->getTopDevice();
90 if (base != top) {
91 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
92 }
93 if (!base->clipIsWideOpen()) {
94 return false;
95 }
reedc83a2972015-07-16 07:40:45 -070096 }
97
98 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070099 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700100 return false; // conservative
101 }
halcanaryc5769b22016-08-10 07:13:21 -0700102
103 SkRect devRect;
104 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
105 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700106 return false;
107 }
108 }
109
110 if (paint) {
111 SkPaint::Style paintStyle = paint->getStyle();
112 if (!(paintStyle == SkPaint::kFill_Style ||
113 paintStyle == SkPaint::kStrokeAndFill_Style)) {
114 return false;
115 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400116 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700117 return false; // conservative
118 }
119 }
120 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
121}
122
123///////////////////////////////////////////////////////////////////////////////////////////////////
124
reed@google.comda17f752012-08-16 18:27:05 +0000125// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126//#define SK_TRACE_SAVERESTORE
127
128#ifdef SK_TRACE_SAVERESTORE
129 static int gLayerCounter;
130 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
131 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
132
133 static int gRecCounter;
134 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
135 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
136
137 static int gCanvasCounter;
138 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
139 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
140#else
141 #define inc_layer()
142 #define dec_layer()
143 #define inc_rec()
144 #define dec_rec()
145 #define inc_canvas()
146 #define dec_canvas()
147#endif
148
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000149typedef SkTLazy<SkPaint> SkLazyPaint;
150
reedc83a2972015-07-16 07:40:45 -0700151void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000152 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700153 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
154 ? SkSurface::kDiscard_ContentChangeMode
155 : SkSurface::kRetain_ContentChangeMode);
156 }
157}
158
159void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
160 ShaderOverrideOpacity overrideOpacity) {
161 if (fSurfaceBase) {
162 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
163 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
164 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
165 // and therefore we don't care which mode we're in.
166 //
167 if (fSurfaceBase->outstandingImageSnapshot()) {
168 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
169 mode = SkSurface::kDiscard_ContentChangeMode;
170 }
171 }
172 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000173 }
174}
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000178/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 The clip/matrix/proc are fields that reflect the top of the save/restore
180 stack. Whenever the canvas changes, it marks a dirty flag, and then before
181 these are used (assuming we're not on a layer) we rebuild these cache
182 values: they reflect the top of the save stack, but translated and clipped
183 by the device's XY offset and bitmap-bounds.
184*/
185struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400186 DeviceCM* fNext;
187 sk_sp<SkBaseDevice> fDevice;
188 SkRasterClip fClip;
189 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
190 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400191 sk_sp<SkImage> fClipImage;
192 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
Florin Malita53f77bd2017-04-28 13:48:37 -0400194 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000195 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700196 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400197 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500198 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700199 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000200 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400201 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400202 {}
reed@google.com4b226022011-01-11 18:32:13 +0000203
mtkleinfeaadee2015-04-08 11:25:48 -0700204 void reset(const SkIRect& bounds) {
205 SkASSERT(!fPaint);
206 SkASSERT(!fNext);
207 SkASSERT(fDevice);
208 fClip.setRect(bounds);
209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210};
211
Mike Reed148b7fd2018-12-18 17:38:18 -0500212namespace {
213// Encapsulate state needed to restore from saveBehind()
214struct BackImage {
215 sk_sp<SkSpecialImage> fImage;
216 SkIPoint fLoc;
217};
218}
219
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220/* This is the record we keep for each save/restore level in the stack.
221 Since a level optionally copies the matrix and/or stack, we have pointers
222 for these fields. If the value is copied for this level, the copy is
223 stored in the ...Storage field, and the pointer points to that. If the
224 value is not copied for this level, we ignore ...Storage, and just point
225 at the corresponding value in the previous level in the stack.
226*/
227class SkCanvas::MCRec {
228public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500229 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 /* If there are any layers in the stack, this points to the top-most
231 one that is at or below this level in the stack (so we know what
232 bitmap/device to draw into from this level. This value is NOT
233 reference counted, since the real owner is either our fLayer field,
234 or a previous one in a lower level.)
235 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500236 DeviceCM* fTopLayer;
237 std::unique_ptr<BackImage> fBackImage;
238 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500239 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500240 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241
Mike Reeda1361362017-03-07 09:37:29 -0500242 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700243 fLayer = nullptr;
244 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800245 fMatrix.reset();
246 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700247
reedd9544982014-09-09 18:46:22 -0700248 // don't bother initializing fNext
249 inc_rec();
250 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400251 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700252 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700253 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800254 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 // don't bother initializing fNext
257 inc_rec();
258 }
259 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700260 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 dec_rec();
262 }
mtkleinfeaadee2015-04-08 11:25:48 -0700263
264 void reset(const SkIRect& bounds) {
265 SkASSERT(fLayer);
266 SkASSERT(fDeferredSaveCount == 0);
267
268 fMatrix.reset();
269 fRasterClip.setRect(bounds);
270 fLayer->reset(bounds);
271 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
Mike Reeda1361362017-03-07 09:37:29 -0500274class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275public:
Mike Reeda1361362017-03-07 09:37:29 -0500276 SkDrawIter(SkCanvas* canvas)
277 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
278 {}
reed@google.com4b226022011-01-11 18:32:13 +0000279
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000281 const DeviceCM* rec = fCurrLayer;
282 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400283 fDevice = rec->fDevice.get();
284 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700286 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 return true;
288 }
289 return false;
290 }
reed@google.com4b226022011-01-11 18:32:13 +0000291
Michael Ludwig915b7792019-10-22 17:40:41 +0000292 int getX() const { return fDevice->getOrigin().x(); }
293 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000295
Mike Reed99330ba2017-02-22 11:01:08 -0500296 SkBaseDevice* fDevice;
297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 const DeviceCM* fCurrLayer;
300 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301};
302
Florin Malita713b8ef2017-04-28 10:57:24 -0400303#define FOR_EACH_TOP_DEVICE( code ) \
304 do { \
305 DeviceCM* layer = fMCRec->fTopLayer; \
306 while (layer) { \
307 SkBaseDevice* device = layer->fDevice.get(); \
308 if (device) { \
309 code; \
310 } \
311 layer = layer->fNext; \
312 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500313 } while (0)
314
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315/////////////////////////////////////////////////////////////////////////////
316
reeddbc3cef2015-04-29 12:18:57 -0700317/**
318 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700319 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700320 */
reedd053ce92016-03-22 10:17:23 -0700321static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700322 SkImageFilter* imgf = paint.getImageFilter();
323 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700324 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700325 }
326
reedd053ce92016-03-22 10:17:23 -0700327 SkColorFilter* imgCFPtr;
328 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700329 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700330 }
reedd053ce92016-03-22 10:17:23 -0700331 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700332
333 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700334 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700335 // there is no existing paint colorfilter, so we can just return the imagefilter's
336 return imgCF;
337 }
338
339 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
340 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500341 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700342}
343
senorblanco87e066e2015-10-28 11:23:36 -0700344/**
345 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
346 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
347 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
348 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
349 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
350 * conservative "effective" bounds based on the settings in the paint... with one exception. This
351 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
352 * deliberately ignored.
353 */
354static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
355 const SkRect& rawBounds,
356 SkRect* storage) {
357 SkPaint tmpUnfiltered(paint);
358 tmpUnfiltered.setImageFilter(nullptr);
359 if (tmpUnfiltered.canComputeFastBounds()) {
360 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
361 } else {
362 return rawBounds;
363 }
364}
365
Mike Reed38992392019-07-30 10:48:15 -0400366class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367public:
senorblanco87e066e2015-10-28 11:23:36 -0700368 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
369 // paint. It's used to determine the size of the offscreen layer for filters.
370 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400371 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
372 bool skipLayerForImageFilter = false,
373 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400375 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700377 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378
Mike Reed38992392019-07-30 10:48:15 -0400379 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
380 SkASSERT(!fLazyPaint.isValid());
381 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700382 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700383 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700384 fPaint = paint;
385 }
386
387 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700388 /**
389 * We implement ImageFilters for a given draw by creating a layer, then applying the
390 * imagefilter to the pixels of that layer (its backing surface/image), and then
391 * we call restore() to xfer that layer to the main canvas.
392 *
393 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
394 * 2. Generate the src pixels:
395 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
396 * return (fPaint). We then draw the primitive (using srcover) into a cleared
397 * buffer/surface.
398 * 3. Restore the layer created in #1
399 * The imagefilter is passed the buffer/surface from the layer (now filled with the
400 * src pixels of the primitive). It returns a new "filtered" buffer, which we
401 * draw onto the previous layer using the xfermode from the original paint.
402 */
Mike Reed38992392019-07-30 10:48:15 -0400403
404 SkPaint restorePaint;
405 restorePaint.setImageFilter(fPaint->refImageFilter());
406 restorePaint.setBlendMode(fPaint->getBlendMode());
407
senorblanco87e066e2015-10-28 11:23:36 -0700408 SkRect storage;
409 if (rawBounds) {
410 // Make rawBounds include all paint outsets except for those due to image filters.
411 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
412 }
Mike Reed38992392019-07-30 10:48:15 -0400413 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700414 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700415 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000416
Mike Reed38992392019-07-30 10:48:15 -0400417 // Remove the restorePaint fields from our "working" paint
418 SkASSERT(!fLazyPaint.isValid());
419 SkPaint* paint = fLazyPaint.set(origPaint);
420 paint->setImageFilter(nullptr);
421 paint->setBlendMode(SkBlendMode::kSrcOver);
422 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423 }
424 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000425
Mike Reed38992392019-07-30 10:48:15 -0400426 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700427 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000428 fCanvas->internalRestore();
429 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000430 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000432
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433 const SkPaint& paint() const {
434 SkASSERT(fPaint);
435 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438private:
Mike Reed38992392019-07-30 10:48:15 -0400439 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000440 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400441 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000442 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700443 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444};
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446////////// macros to place around the internal draw calls //////////////////
447
Mike Reed38992392019-07-30 10:48:15 -0400448#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700449 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400450 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
451 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800452
453
Mike Reed38992392019-07-30 10:48:15 -0400454#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000455 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400456 AutoLayerForImageFilter draw(this, paint, true); \
457 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000458
Mike Reed38992392019-07-30 10:48:15 -0400459#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000460 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400461 AutoLayerForImageFilter draw(this, paint, false, bounds); \
462 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000463
Mike Reed38992392019-07-30 10:48:15 -0400464#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700465 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400466 AutoLayerForImageFilter draw(this, paint, false, bounds); \
467 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700468
Mike Reed38992392019-07-30 10:48:15 -0400469#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470
471////////////////////////////////////////////////////////////////////////////
472
msarettfbfa2582016-08-12 08:29:08 -0700473static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
474 if (bounds.isEmpty()) {
475 return SkRect::MakeEmpty();
476 }
477
478 // Expand bounds out by 1 in case we are anti-aliasing. We store the
479 // bounds as floats to enable a faster quick reject implementation.
480 SkRect dst;
481 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
482 return dst;
483}
484
mtkleinfeaadee2015-04-08 11:25:48 -0700485void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
486 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700487 fMCRec->reset(bounds);
488
489 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500490 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400491 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700492 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700493 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700494}
495
Hal Canary363a3f82018-10-04 11:04:48 -0400496void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800497 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500500 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500501 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700502 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503
reeda499f902015-05-01 09:34:31 -0700504 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
505 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400506 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509
halcanary96fcdcc2015-08-27 07:41:13 -0700510 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000511
reedf92c8662014-08-18 08:02:43 -0700512 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700513 // The root device and the canvas should always have the same pixel geometry
514 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800515 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700516 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700519 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400520
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500521 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
reed@google.comcde92112011-07-06 20:00:52 +0000524SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700526 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000527{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000528 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000529
Hal Canary363a3f82018-10-04 11:04:48 -0400530 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000531}
532
reed96a857e2015-01-25 10:33:58 -0800533SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800535 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000536{
537 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400538 this->init(sk_make_sp<SkNoPixelsDevice>(
Hal Canary363a3f82018-10-04 11:04:48 -0400539 SkIRect::MakeWH(SkTMax(width, 0), SkTMax(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700540}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000541
Hal Canary363a3f82018-10-04 11:04:48 -0400542SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700543 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700544 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700545{
546 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700547
Mike Reed566e53c2017-03-10 10:49:45 -0500548 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400549 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700550}
551
Herb Derbyefe39bc2018-05-01 17:06:20 -0400552SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000553 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700554 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000555{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700557
Hal Canary363a3f82018-10-04 11:04:48 -0400558 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700559}
560
reed4a8126e2014-09-22 07:29:03 -0700561SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700562 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700563 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700564{
565 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700566
Mike Reed910ca0f2018-04-25 13:04:05 -0400567 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400568 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700569}
reed29c857d2014-09-21 10:25:07 -0700570
Mike Reed356f7c22017-01-10 11:58:39 -0500571SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
572 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700573 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
574 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500575 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700576{
577 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700578
Mike Reed910ca0f2018-04-25 13:04:05 -0400579 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400580 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581}
582
Mike Reed356f7c22017-01-10 11:58:39 -0500583SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
584
Matt Sarett31f99ce2017-04-11 08:46:01 -0400585#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
586SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
587 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
588 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
589 , fAllocator(nullptr)
590{
591 inc_canvas();
592
593 SkBitmap tmp(bitmap);
594 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400595 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400596 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400597}
598#endif
599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600SkCanvas::~SkCanvas() {
601 // free up the contents of our deque
602 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 this->internalRestore(); // restore the last, since we're going away
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 dec_canvas();
607}
608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609///////////////////////////////////////////////////////////////////////////////
610
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000611void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700612 this->onFlush();
613}
614
615void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000616 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000617 if (device) {
618 device->flush();
619 }
620}
621
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500622SkSurface* SkCanvas::getSurface() const {
623 return fSurfaceBase;
624}
625
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000626SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000627 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000628 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
629}
630
senorblancoafc7cce2016-02-02 18:44:15 -0800631SkIRect SkCanvas::getTopLayerBounds() const {
632 SkBaseDevice* d = this->getTopDevice();
633 if (!d) {
634 return SkIRect::MakeEmpty();
635 }
Michael Ludwig915b7792019-10-22 17:40:41 +0000636 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
senorblancoafc7cce2016-02-02 18:44:15 -0800637}
638
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000639SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000641 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400643 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644}
645
Florin Malita0ed3b642017-01-13 16:56:38 +0000646SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400647 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000648}
649
Mike Reed353196f2017-07-21 11:01:18 -0400650bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000651 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400652 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000653}
654
Mike Reed353196f2017-07-21 11:01:18 -0400655bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
656 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400657}
658
659bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
660 SkPixmap pm;
661 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
662}
663
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000664bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400665 SkPixmap pm;
666 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700667 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000668 }
669 return false;
670}
671
Matt Sarett03dd6d52017-01-23 12:15:09 -0500672bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 SkBaseDevice* device = this->getDevice();
675 if (!device) {
676 return false;
677 }
678
Matt Sarett03dd6d52017-01-23 12:15:09 -0500679 // This check gives us an early out and prevents generation ID churn on the surface.
680 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
681 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400682 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500683 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685
Matt Sarett03dd6d52017-01-23 12:15:09 -0500686 // Tell our owning surface to bump its generation ID.
687 const bool completeOverwrite =
688 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700689 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700690
Matt Sarett03dd6d52017-01-23 12:15:09 -0500691 // This can still fail, most notably in the case of a invalid color type or alpha type
692 // conversion. We could pull those checks into this function and avoid the unnecessary
693 // generation ID bump. But then we would be performing those checks twice, since they
694 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400695 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000696}
reed@google.com51df9e32010-12-23 19:29:18 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698//////////////////////////////////////////////////////////////////////////////
699
reed2ff1fce2014-12-11 07:07:37 -0800700void SkCanvas::checkForDeferredSave() {
701 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800702 this->doSave();
703 }
704}
705
reedf0090cb2014-11-26 08:55:51 -0800706int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800707#ifdef SK_DEBUG
708 int count = 0;
709 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
710 for (;;) {
711 const MCRec* rec = (const MCRec*)iter.next();
712 if (!rec) {
713 break;
714 }
715 count += 1 + rec->fDeferredSaveCount;
716 }
717 SkASSERT(count == fSaveCount);
718#endif
719 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800720}
721
722int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800723 fSaveCount += 1;
724 fMCRec->fDeferredSaveCount += 1;
725 return this->getSaveCount() - 1; // return our prev value
726}
727
728void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800729 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700730
731 SkASSERT(fMCRec->fDeferredSaveCount > 0);
732 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800733 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800734}
735
Mike Reedee0a03a2020-01-14 16:44:47 -0500736int SkCanvas::saveCamera(const SkMatrix44& projection, const SkMatrix44& camera) {
737 // TODO: add a virtual for this, and update clients (e.g. chrome)
738 int n = this->save();
Mike Reedb18e74d2020-01-16 13:58:22 -0500739 this->concat(projection * camera);
740 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500741 return n;
742}
743
reedf0090cb2014-11-26 08:55:51 -0800744void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800745 if (fMCRec->fDeferredSaveCount > 0) {
746 SkASSERT(fSaveCount > 1);
747 fSaveCount -= 1;
748 fMCRec->fDeferredSaveCount -= 1;
749 } else {
750 // check for underflow
751 if (fMCStack.count() > 1) {
752 this->willRestore();
753 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700754 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800755 this->internalRestore();
756 this->didRestore();
757 }
reedf0090cb2014-11-26 08:55:51 -0800758 }
759}
760
761void SkCanvas::restoreToCount(int count) {
762 // sanity check
763 if (count < 1) {
764 count = 1;
765 }
mtkleinf0f14112014-12-12 08:46:25 -0800766
reedf0090cb2014-11-26 08:55:51 -0800767 int n = this->getSaveCount() - count;
768 for (int i = 0; i < n; ++i) {
769 this->restore();
770 }
771}
772
reed2ff1fce2014-12-11 07:07:37 -0800773void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700775 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000777
Mike Reedc42a1cd2017-02-14 14:25:14 -0500778 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779}
780
reed4960eee2015-12-18 07:09:18 -0800781bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400782 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783}
784
reed4960eee2015-12-18 07:09:18 -0800785bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700786 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400787 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
788 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
789 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
790 // filter does not require a decomposed CTM matrix, the filter space and device space are the
791 // same. When it has been decomposed, we want the original image filter node to process the
792 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
793 // longer the original filter, but has the remainder matrix baked into it, and passing in the
794 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
795 // to the original filter node (barring inflation from consecutive calls to mapRect). While
796 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
797 // passing getDeviceClipBounds() to 'imageFilter' is correct.
798 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
799 // be important to more accurately calculate the clip bounds in the layer space for the original
800 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500801 SkIRect clipBounds = this->getDeviceClipBounds();
802 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000803 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000804 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000805
reed96e657d2015-03-10 17:30:07 -0700806 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
807
Robert Phillips12078432018-05-17 11:17:39 -0400808 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
809 // If the image filter DAG affects transparent black then we will need to render
810 // out to the clip bounds
811 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000812 }
Robert Phillips12078432018-05-17 11:17:39 -0400813
814 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700815 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700817 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400818 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400820 inputSaveLayerBounds = clipBounds;
821 }
822
823 if (imageFilter) {
824 // expand the clip bounds by the image filter DAG to include extra content that might
825 // be required by the image filters.
826 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
827 SkImageFilter::kReverse_MapDirection,
828 &inputSaveLayerBounds);
829 }
830
831 SkIRect clippedSaveLayerBounds;
832 if (bounds) {
833 // For better or for worse, user bounds currently act as a hard clip on the layer's
834 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
835 clippedSaveLayerBounds = inputSaveLayerBounds;
836 } else {
837 // If there are no user bounds, we don't want to artificially restrict the resulting
838 // layer bounds, so allow the expanded clip bounds free reign.
839 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800841
842 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400843 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800844 if (BoundsAffectsClip(saveLayerFlags)) {
845 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
846 fMCRec->fRasterClip.setEmpty();
847 fDeviceClipBounds.setEmpty();
848 }
849 return false;
850 }
Robert Phillips12078432018-05-17 11:17:39 -0400851 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852
reed4960eee2015-12-18 07:09:18 -0800853 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700854 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400855 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
856 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000857 }
858
859 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400860 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000861 }
Robert Phillips12078432018-05-17 11:17:39 -0400862
junov@chromium.orga907ac32012-02-24 21:54:07 +0000863 return true;
864}
865
reed4960eee2015-12-18 07:09:18 -0800866int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
867 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000868}
869
Cary Clarke041e312018-03-06 13:00:52 -0500870int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700871 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400872 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
873 // no need for the layer (or any of the draws until the matching restore()
874 this->save();
875 this->clipRect({0,0,0,0});
876 } else {
877 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
878 fSaveCount += 1;
879 this->internalSaveLayer(rec, strategy);
880 }
reed4960eee2015-12-18 07:09:18 -0800881 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800882}
883
Mike Reed148b7fd2018-12-18 17:38:18 -0500884int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
885 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
886 // Assuming clips never expand, if the request bounds is outside of the current clip
887 // there is no need to copy/restore the area, so just devolve back to a regular save.
888 this->save();
889 } else {
890 bool doTheWork = this->onDoSaveBehind(bounds);
891 fSaveCount += 1;
892 this->internalSave();
893 if (doTheWork) {
894 this->internalSaveBehind(bounds);
895 }
896 }
897 return this->getSaveCount() - 1;
898}
899
reeda2217ef2016-07-20 06:04:34 -0700900void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500901 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500902 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400903 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
904 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400905 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig915b7792019-10-22 17:40:41 +0000906
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400907 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400908 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
909 // This means that we only have to copy a dst-sized block of pixels out of src and translate
910 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400911 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
912 dstOrigin.y() - src->getOrigin().y(),
913 dst->width(), dst->height());
914 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400915 return;
916 }
917
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400918 auto special = src->snapSpecial(snapBounds);
919 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400920 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
921 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400922 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
923 }
924 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400925 }
926
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400927 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
928 // by the backdrop filter.
929 SkMatrix toRoot, layerMatrix;
930 SkSize scale;
931 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
932 toRoot = SkMatrix::I();
933 layerMatrix = ctm;
934 } else if (ctm.decomposeScale(&scale, &toRoot)) {
935 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
936 } else {
937 // Perspective, for now, do no scaling of the layer itself.
938 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
939 // the matrix, e.g. based on the midpoint of the near/far planes?
940 toRoot = ctm;
941 layerMatrix = SkMatrix::I();
942 }
943
944 // We have to map the dst bounds from the root space into the layer space where filtering will
945 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
946 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
947 // is a safe, conservative estimate.
948 SkMatrix fromRoot;
949 if (!toRoot.invert(&fromRoot)) {
950 return;
951 }
952
953 // This represents what the backdrop filter needs to produce in the layer space, and is sized
954 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
955 SkIRect layerTargetBounds = fromRoot.mapRect(
956 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
957 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
958 // require some extra input pixels.
959 SkIRect layerInputBounds = filter->filterBounds(
960 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
961 &layerTargetBounds);
962
963 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400964 // be the conservative contents required to fill a layerInputBounds-sized surface with the
965 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400966 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
967 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
968 if (!backdropBounds.intersect(srcDevRect)) {
969 return;
970 }
971
972 auto special = src->snapSpecial(backdropBounds);
973 if (!special) {
974 return;
975 }
976
977 SkColorType colorType = src->imageInfo().colorType();
978 if (colorType == kUnknown_SkColorType) {
979 colorType = kRGBA_8888_SkColorType;
980 }
981 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400982
983 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400984 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400985 // Drawing the temporary and final filtered image requires a higher filter quality if the
986 // 'toRoot' transformation is not identity, in order to minimize the impact on already
987 // rendered edges/content.
988 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
989 p.setFilterQuality(kHigh_SkFilterQuality);
990
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400991 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
992 // and stored in a temporary surface, which is then used as the input to the actual filter.
993 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
994 if (!tmpSurface) {
995 return;
996 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400997
998 auto tmpCanvas = tmpSurface->getCanvas();
999 tmpCanvas->clear(SK_ColorTRANSPARENT);
1000 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1001 // space, then maps from root space into the layer space, then maps it so the input layer's
1002 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1003 // performed on backdropBounds.
1004 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1005 tmpCanvas->concat(fromRoot);
1006 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001007
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001008 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1009 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1010 special = tmpSurface->makeImageSnapshot();
1011 } else {
1012 // Since there is no extra transform that was done, update the input bounds to reflect
1013 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1014 // was equal to backdropBounds before it was made relative to the src device and cropped.
1015 // When we use the original snapped image directly, just map the update backdrop bounds
1016 // back into the shared layer space
1017 layerInputBounds = backdropBounds;
1018 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001019
1020 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1021 // draw will be 1-1 so there is no need to increase filter quality.
1022 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001023 }
1024
1025 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1026 // layer space. This has to further offset everything so that filter evaluation thinks the
1027 // source image's top left corner is (0, 0).
1028 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1029 // this can be simplified.
1030 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1031 SkMatrix filterCTM = layerMatrix;
1032 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1033 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1034
1035 SkIPoint offset;
1036 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001037 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001038 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1039 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1040 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1041 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001042 offset += layerInputBounds.topLeft();
1043
1044 // Manually setting the device's CTM requires accounting for the device's origin.
1045 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001046 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001047 // a global CTM instead of a device CTM.
1048 SkMatrix dstCTM = toRoot;
1049 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001050 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001051
1052 // And because devices don't have a special-image draw function that supports arbitrary
1053 // matrices, we are abusing the asImage() functionality here...
1054 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001055 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001056 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001057 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001058 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1059 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001060 }
robertphillips7354a4b2015-12-16 05:08:27 -08001061}
reed70ee31b2015-12-10 13:44:45 -08001062
Mike Kleine083f7c2018-02-07 12:54:27 -05001063static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001064 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001065 if (prev.bytesPerPixel() <= 4 &&
1066 prev.colorType() != kRGBA_8888_SkColorType &&
1067 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001068 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1069 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1070 ct = kN32_SkColorType;
1071 }
1072 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001073}
1074
reed4960eee2015-12-18 07:09:18 -08001075void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001076 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001077 const SkRect* bounds = rec.fBounds;
1078 const SkPaint* paint = rec.fPaint;
1079 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1080
Mike Reed5532c2a2019-02-23 12:00:32 -05001081 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1082 // regardless of any hint-rect from the caller. skbug.com/8783
1083 if (rec.fBackdrop) {
1084 bounds = nullptr;
1085 }
1086
reed8c30a812016-04-20 16:36:51 -07001087 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001088 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001089 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001090 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001091
reed8c30a812016-04-20 16:36:51 -07001092 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001093 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1094 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1095 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001096 *
1097 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001098 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1099 * if necessary.
1100 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1101 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001102 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
Michael Ludwig08b260c2019-05-17 11:21:53 -04001103 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001104 * of the original imagefilter, and draw that (via drawSprite)
1105 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1106 *
1107 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1108 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1109 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001110 if (imageFilter) {
1111 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001112 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1113 &modifiedCTM);
1114 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001115 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001116 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001117 modifiedRec = fMCRec;
1118 this->internalSetMatrix(modifiedCTM);
1119 SkPaint* p = lazyP.set(*paint);
1120 p->setImageFilter(std::move(modifiedFilter));
1121 imageFilter = p->getImageFilter();
1122 paint = p;
1123 }
1124 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1125 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001126 }
reed8c30a812016-04-20 16:36:51 -07001127
junov@chromium.orga907ac32012-02-24 21:54:07 +00001128 // do this before we create the layer. We don't call the public save() since
1129 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001130 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001131
junov@chromium.orga907ac32012-02-24 21:54:07 +00001132 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001133 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001134 if (modifiedRec) {
1135 // In this case there will be no layer in which to stash the matrix so we need to
1136 // revert the prior MCRec to its earlier state.
1137 modifiedRec->fMatrix = stashedMatrix;
1138 }
reed2ff1fce2014-12-11 07:07:37 -08001139 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 }
1141
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001142 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1143 // the clipRectBounds() call above?
1144 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001145 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001146 }
1147
reed8dc0ccb2015-03-20 06:32:52 -07001148 SkPixelGeometry geo = fProps.pixelGeometry();
1149 if (paint) {
reed76033be2015-03-14 10:54:31 -07001150 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001151 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001152 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001153 }
1154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155
robertphillips5139e502016-07-19 05:10:40 -07001156 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001157 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001158 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001159 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001160 }
reedb2db8982014-11-13 12:41:02 -08001161
Mike Kleine083f7c2018-02-07 12:54:27 -05001162 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001163 if (rec.fSaveLayerFlags & kF16ColorType) {
1164 info = info.makeColorType(kRGBA_F16_SkColorType);
1165 }
reed129ed1c2016-02-22 06:42:31 -08001166
Hal Canary704cd322016-11-07 14:13:52 -05001167 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001168 {
Florin Malita4571e492019-07-16 10:25:58 -04001169 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001170 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001171 const bool trackCoverage =
1172 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001173 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001174 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001175 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001176 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1177 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001178 return;
reed61f501f2015-04-29 08:34:00 -07001179 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001180 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001181 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182
Mike Reedb43a3e02017-02-11 10:18:58 -05001183 // only have a "next" if this new layer doesn't affect the clip (rare)
1184 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 fMCRec->fLayer = layer;
1186 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001187
Mike Reedc61abee2017-02-28 17:45:27 -05001188 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001189 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001190 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001191 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001192
Mike Reedc42a1cd2017-02-14 14:25:14 -05001193 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1194
1195 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1196 if (layer->fNext) {
1197 // need to punch a hole in the previous device, so we don't draw there, given that
1198 // the new top-layer will allow drawing to happen "below" it.
1199 SkRegion hole(ir);
1200 do {
1201 layer = layer->fNext;
1202 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1203 } while (layer->fNext);
1204 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205}
1206
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001207int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001208 if (0xFF == alpha) {
1209 return this->saveLayer(bounds, nullptr);
1210 } else {
1211 SkPaint tmpPaint;
1212 tmpPaint.setAlpha(alpha);
1213 return this->saveLayer(bounds, &tmpPaint);
1214 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001215}
1216
Mike Reed148b7fd2018-12-18 17:38:18 -05001217void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001218 SkIRect devBounds;
1219 if (localBounds) {
1220 SkRect tmp;
1221 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1222 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1223 devBounds.setEmpty();
1224 }
1225 } else {
1226 devBounds = this->getDeviceClipBounds();
1227 }
1228 if (devBounds.isEmpty()) {
1229 return;
1230 }
1231
Mike Reed148b7fd2018-12-18 17:38:18 -05001232 SkBaseDevice* device = this->getTopDevice();
1233 if (nullptr == device) { // Do we still need this check???
1234 return;
1235 }
1236
Michael Ludwig915b7792019-10-22 17:40:41 +00001237 // need the bounds relative to the device itself
1238 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
Mike Reed148b7fd2018-12-18 17:38:18 -05001239
Michael Ludwigac352122019-08-28 21:03:05 +00001240 // This is getting the special image from the current device, which is then drawn into (both by
1241 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1242 // own device, we need to explicitly copy the back image contents so that its original content
1243 // is available when we splat it back later during restore.
1244 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001245 if (!backImage) {
1246 return;
1247 }
1248
1249 // we really need the save, so we can wack the fMCRec
1250 this->checkForDeferredSave();
1251
1252 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1253
1254 SkPaint paint;
1255 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001256 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001257}
1258
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259void SkCanvas::internalRestore() {
1260 SkASSERT(fMCStack.count() != 0);
1261
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001262 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 DeviceCM* layer = fMCRec->fLayer; // may be null
1264 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001265 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266
Mike Reed148b7fd2018-12-18 17:38:18 -05001267 // move this out before we do the actual restore
1268 auto backImage = std::move(fMCRec->fBackImage);
1269
Mike Reedb18e74d2020-01-16 13:58:22 -05001270 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1271 fCameraStack.pop_back();
1272 }
1273
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 // now do the normal restore()
1275 fMCRec->~MCRec(); // balanced in save()
1276 fMCStack.pop_back();
1277 fMCRec = (MCRec*)fMCStack.back();
1278
Mike Reedc42a1cd2017-02-14 14:25:14 -05001279 if (fMCRec) {
1280 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1281 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001282
Mike Reed148b7fd2018-12-18 17:38:18 -05001283 if (backImage) {
1284 SkPaint paint;
1285 paint.setBlendMode(SkBlendMode::kDstOver);
1286 const int x = backImage->fLoc.x();
1287 const int y = backImage->fLoc.y();
1288 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1289 nullptr, SkMatrix::I());
1290 }
1291
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1293 since if we're being recorded, we don't want to record this (the
1294 recorder will have already recorded the restore).
1295 */
bsalomon49f085d2014-09-05 13:34:00 -07001296 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001297 if (fMCRec) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001298 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001299 layer->fDevice->setImmutable();
Michael Ludwig915b7792019-10-22 17:40:41 +00001300 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
1301 layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001302 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001303 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001304 this->internalSetMatrix(layer->fStashedMatrix);
Michael Ludwig915b7792019-10-22 17:40:41 +00001305 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001306 delete layer;
reedb679ca82015-04-07 04:40:48 -07001307 } else {
1308 // we're at the root
reeda499f902015-05-01 09:34:31 -07001309 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001310 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001311 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001313 }
msarettfbfa2582016-08-12 08:29:08 -07001314
1315 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001316 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001317 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319}
1320
reede8f30622016-03-23 18:59:25 -07001321sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001322 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001323 props = &fProps;
1324 }
1325 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001326}
1327
reede8f30622016-03-23 18:59:25 -07001328sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001329 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001330 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001331}
1332
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001333SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001334 return this->onImageInfo();
1335}
1336
1337SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001338 SkBaseDevice* dev = this->getDevice();
1339 if (dev) {
1340 return dev->imageInfo();
1341 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001342 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001343 }
1344}
1345
brianosman898235c2016-04-06 07:38:23 -07001346bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001347 return this->onGetProps(props);
1348}
1349
1350bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001351 SkBaseDevice* dev = this->getDevice();
1352 if (dev) {
1353 if (props) {
1354 *props = fProps;
1355 }
1356 return true;
1357 } else {
1358 return false;
1359 }
1360}
1361
reed6ceeebd2016-03-09 14:26:26 -08001362bool SkCanvas::peekPixels(SkPixmap* pmap) {
1363 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001364}
1365
reed884e97c2015-05-26 11:31:54 -07001366bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001367 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001368 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001369}
1370
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001371void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001372 SkPixmap pmap;
1373 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001374 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001375 }
1376 if (info) {
1377 *info = pmap.info();
1378 }
1379 if (rowBytes) {
1380 *rowBytes = pmap.rowBytes();
1381 }
1382 if (origin) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001383 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001384 }
reed884e97c2015-05-26 11:31:54 -07001385 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001386}
1387
reed884e97c2015-05-26 11:31:54 -07001388bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001389 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001390 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001391}
1392
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394
Mike Reed8bcd1282019-03-13 16:51:54 -04001395// In our current design/features, we should never have a layer (src) in a different colorspace
1396// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1397// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1398// colorspace.
1399static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1400 SkASSERT(src == dst);
1401}
1402
Michael Ludwig915b7792019-10-22 17:40:41 +00001403void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001404 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001406 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 paint = &tmp;
1408 }
reed@google.com4b226022011-01-11 18:32:13 +00001409
Mike Reed38992392019-07-30 10:48:15 -04001410 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001413 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001414 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1415 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001416 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001417 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwig915b7792019-10-22 17:40:41 +00001418 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001419 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001420 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1421 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001422 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1423 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001424 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1425 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001426 }
reed@google.com76dd2772012-01-05 21:15:07 +00001427 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001428 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001429 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 }
reeda2217ef2016-07-20 06:04:34 -07001431
Mike Reed38992392019-07-30 10:48:15 -04001432 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433}
1434
reed32704672015-12-16 08:27:10 -08001435/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001436
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001437void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001438 if (dx || dy) {
1439 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001440 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001441
reedfe69b502016-09-12 06:31:48 -07001442 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001443 // However, if either is non-finite, we might still complicate the matrix type,
1444 // so we still have to compute this.
1445 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001446
Mike Reedc42a1cd2017-02-14 14:25:14 -05001447 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001448
reedfe69b502016-09-12 06:31:48 -07001449 this->didTranslate(dx,dy);
1450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001453void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001454#ifdef SK_SUPPORT_LEGACY_CANVAS_MATRIX_VIRTUALS
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001455 SkMatrix m;
1456 m.setScale(sx, sy);
1457 this->concat(m);
Mike Reed9403c382020-01-13 14:40:56 +00001458#else
1459 if (sx != 1 || sy != 1) {
1460 this->checkForDeferredSave();
1461 fMCRec->fMatrix.preScale(sx, sy);
1462
1463 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1464 // but pre-scaling by a non-finite does change it, so we have to recompute.
1465 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1466
1467 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1468
1469 this->didScale(sx, sy);
1470 }
1471#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001475 SkMatrix m;
1476 m.setRotate(degrees);
1477 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
bungeman7438bfc2016-07-12 15:01:19 -07001480void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1481 SkMatrix m;
1482 m.setRotate(degrees, px, py);
1483 this->concat(m);
1484}
1485
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001486void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001487 SkMatrix m;
1488 m.setSkew(sx, sy);
1489 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001490}
1491
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001492void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001493 if (matrix.isIdentity()) {
1494 return;
1495 }
1496
reed2ff1fce2014-12-11 07:07:37 -08001497 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001498 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001499
msarett9637ea92016-08-18 14:03:30 -07001500 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001501
Mike Reed7627fa52017-02-08 10:07:53 -05001502 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001503
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001504 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505}
1506
Mike Reed064c7f92020-01-08 17:33:04 -05001507void SkCanvas::concat44(const SkScalar m[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001508 this->checkForDeferredSave();
1509
Mike Reedb18e74d2020-01-16 13:58:22 -05001510 fMCRec->fMatrix.preConcat16(m);
Mike Reed403c8072020-01-08 10:40:39 -05001511
1512 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1513
1514 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1515
1516 this->didConcat44(m);
1517}
Mike Reed064c7f92020-01-08 17:33:04 -05001518
1519void SkCanvas::concat(const SkMatrix44& m) {
1520 this->concat44(m.values());
1521}
Mike Reed403c8072020-01-08 10:40:39 -05001522
reed8c30a812016-04-20 16:36:51 -07001523void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001524 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001525 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001526
Mike Reedc42a1cd2017-02-14 14:25:14 -05001527 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001528}
1529
1530void SkCanvas::setMatrix(const SkMatrix& matrix) {
1531 this->checkForDeferredSave();
1532 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001533 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534}
1535
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001537 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538}
1539
1540//////////////////////////////////////////////////////////////////////////////
1541
Mike Reedc1f77742016-12-09 09:00:50 -05001542void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001543 if (!rect.isFinite()) {
1544 return;
1545 }
reed2ff1fce2014-12-11 07:07:37 -08001546 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001547 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1548 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001549}
1550
Mike Reedc1f77742016-12-09 09:00:50 -05001551void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001552 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001553
Mike Reed7627fa52017-02-08 10:07:53 -05001554 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001555
reedc64eff52015-11-21 12:39:45 -08001556 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001557 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1558 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001559 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001560}
1561
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001562void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1563 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001564 if (fClipRestrictionRect.isEmpty()) {
1565 // we notify the device, but we *dont* resolve deferred saves (since we're just
1566 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001567 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001568 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001569 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001570 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001571 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001572 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001573 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1574 }
1575}
1576
Mike Reedc1f77742016-12-09 09:00:50 -05001577void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001578 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001580 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001581 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1582 } else {
1583 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001584 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001586
Mike Reedc1f77742016-12-09 09:00:50 -05001587void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001588 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001589
Brian Salomona3b45d42016-10-03 11:36:16 -04001590 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001591
Mike Reed7627fa52017-02-08 10:07:53 -05001592 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001593
Mike Reed20800c82017-11-15 16:09:04 -05001594 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1595 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001596 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001597}
1598
Mike Reedc1f77742016-12-09 09:00:50 -05001599void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001600 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001601 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001602
1603 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1604 SkRect r;
1605 if (path.isRect(&r)) {
1606 this->onClipRect(r, op, edgeStyle);
1607 return;
1608 }
1609 SkRRect rrect;
1610 if (path.isOval(&r)) {
1611 rrect.setOval(r);
1612 this->onClipRRect(rrect, op, edgeStyle);
1613 return;
1614 }
1615 if (path.isRRect(&rrect)) {
1616 this->onClipRRect(rrect, op, edgeStyle);
1617 return;
1618 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001619 }
robertphillips39f05382015-11-24 09:30:12 -08001620
1621 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622}
1623
Mike Reedc1f77742016-12-09 09:00:50 -05001624void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001625 AutoValidateClip avc(this);
1626
Brian Salomona3b45d42016-10-03 11:36:16 -04001627 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001628
Mike Reed7627fa52017-02-08 10:07:53 -05001629 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630
Brian Salomona3b45d42016-10-03 11:36:16 -04001631 const SkPath* rasterClipPath = &path;
Mike Reed403c8072020-01-08 10:40:39 -05001632 fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
Mike Reed20800c82017-11-15 16:09:04 -05001633 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001634 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635}
1636
Mike Reedc1f77742016-12-09 09:00:50 -05001637void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001638 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001639 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640}
1641
Mike Reedc1f77742016-12-09 09:00:50 -05001642void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001643 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001644
reed@google.com5c3d1472011-02-22 19:12:23 +00001645 AutoValidateClip avc(this);
1646
Mike Reed20800c82017-11-15 16:09:04 -05001647 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001648 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649}
1650
reed@google.com819c9212011-02-23 18:56:55 +00001651#ifdef SK_DEBUG
1652void SkCanvas::validateClip() const {
1653 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001654 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001655 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001656 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001657 return;
1658 }
reed@google.com819c9212011-02-23 18:56:55 +00001659}
1660#endif
1661
Mike Reeda1361362017-03-07 09:37:29 -05001662bool SkCanvas::androidFramework_isClipAA() const {
1663 bool containsAA = false;
1664
1665 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1666
1667 return containsAA;
1668}
1669
1670class RgnAccumulator {
1671 SkRegion* fRgn;
1672public:
1673 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1674 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1675 SkIPoint origin = device->getOrigin();
1676 if (origin.x() | origin.y()) {
1677 rgn->translate(origin.x(), origin.y());
1678 }
1679 fRgn->op(*rgn, SkRegion::kUnion_Op);
1680 }
1681};
1682
1683void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1684 RgnAccumulator accum(rgn);
1685 SkRegion tmp;
1686
1687 rgn->setEmpty();
1688 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001689}
1690
reed@google.com5c3d1472011-02-22 19:12:23 +00001691///////////////////////////////////////////////////////////////////////////////
1692
reed@google.com754de5f2014-02-24 19:38:20 +00001693bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001694 return fMCRec->fRasterClip.isEmpty();
1695
1696 // TODO: should we only use the conservative answer in a recording canvas?
1697#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001698 SkBaseDevice* dev = this->getTopDevice();
1699 // if no device we return true
1700 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001701#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001702}
1703
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001704bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001705 SkBaseDevice* dev = this->getTopDevice();
1706 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001707 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001708}
1709
msarettfbfa2582016-08-12 08:29:08 -07001710static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1711#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1712 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1713 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1714 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1715 return 0xF != _mm_movemask_ps(mask);
1716#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1717 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1718 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1719 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1720 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1721#else
1722 SkRect devRectAsRect;
1723 SkRect devClipAsRect;
1724 devRect.store(&devRectAsRect.fLeft);
1725 devClip.store(&devClipAsRect.fLeft);
1726 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1727#endif
1728}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001729
msarettfbfa2582016-08-12 08:29:08 -07001730// It's important for this function to not be inlined. Otherwise the compiler will share code
1731// between the fast path and the slow path, resulting in two slow paths.
1732static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1733 const SkMatrix& matrix) {
1734 SkRect deviceRect;
1735 matrix.mapRect(&deviceRect, src);
1736 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1737}
1738
1739bool SkCanvas::quickReject(const SkRect& src) const {
1740#ifdef SK_DEBUG
1741 // Verify that fDeviceClipBounds are set properly.
1742 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001743 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001744 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001745 } else {
msarettfbfa2582016-08-12 08:29:08 -07001746 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747 }
msarettfbfa2582016-08-12 08:29:08 -07001748
msarett9637ea92016-08-18 14:03:30 -07001749 // Verify that fIsScaleTranslate is set properly.
1750 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001751#endif
1752
msarett9637ea92016-08-18 14:03:30 -07001753 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001754 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1755 }
1756
1757 // We inline the implementation of mapScaleTranslate() for the fast path.
1758 float sx = fMCRec->fMatrix.getScaleX();
1759 float sy = fMCRec->fMatrix.getScaleY();
1760 float tx = fMCRec->fMatrix.getTranslateX();
1761 float ty = fMCRec->fMatrix.getTranslateY();
1762 Sk4f scale(sx, sy, sx, sy);
1763 Sk4f trans(tx, ty, tx, ty);
1764
1765 // Apply matrix.
1766 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1767
1768 // Make sure left < right, top < bottom.
1769 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1770 Sk4f min = Sk4f::Min(ltrb, rblt);
1771 Sk4f max = Sk4f::Max(ltrb, rblt);
1772 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1773 // ARM this sequence generates the fastest (a single instruction).
1774 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1775
1776 // Check if the device rect is NaN or outside the clip.
1777 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778}
1779
reed@google.com3b3e8952012-08-16 20:53:31 +00001780bool SkCanvas::quickReject(const SkPath& path) const {
1781 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782}
1783
Mike Klein83c8dd92017-11-28 17:08:45 -05001784SkRect SkCanvas::getLocalClipBounds() const {
1785 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001786 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001787 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788 }
1789
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001790 SkMatrix inverse;
1791 // if we can't invert the CTM, we can't return local clip bounds
Mike Reedb18e74d2020-01-16 13:58:22 -05001792 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001793 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001794 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795
Mike Reed42e8c532017-01-23 14:09:13 -05001796 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001797 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001798 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001799
Mike Reedb57b9312018-04-23 12:12:54 -04001800 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001801 inverse.mapRect(&bounds, r);
1802 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803}
1804
Mike Klein83c8dd92017-11-28 17:08:45 -05001805SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001806 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001807}
1808
Mike Reedb18e74d2020-01-16 13:58:22 -05001809///////////////////////////////////////////////////////////////////////
1810
1811SkCanvas::CameraRec::CameraRec(MCRec* owner, const SkM44& camera)
1812 : fMCRec(owner)
1813 , fCamera(camera)
1814{
1815 // assumes the mcrec has already been concatenated with the camera
1816 if (!owner->fMatrix.invert(&fInvPostCamera)) {
1817 fInvPostCamera.setIdentity();
1818 }
1819}
1820
Mike Reed403c8072020-01-08 10:40:39 -05001821SkMatrix SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001822 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823}
1824
Mike Reedb18e74d2020-01-16 13:58:22 -05001825SkM44 SkCanvas::getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001826 return fMCRec->fMatrix;
1827}
1828
Mike Reedb18e74d2020-01-16 13:58:22 -05001829SkM44 SkCanvas::getLocalToWorld() const {
1830 if (fCameraStack.empty()) {
1831 return this->getLocalToDevice();
1832 } else {
1833 const auto& top = fCameraStack.back();
1834 return top.fInvPostCamera * this->getLocalToDevice();
1835 }
1836}
1837
1838SkM44 SkCanvas::getLocalToCamera() const {
1839 if (fCameraStack.empty()) {
1840 return this->getLocalToDevice();
1841 } else {
1842 const auto& top = fCameraStack.back();
1843 return top.fCamera * top.fInvPostCamera * this->getLocalToDevice();
1844 }
1845}
1846
Brian Osman11052242016-10-27 14:47:55 -04001847GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001848 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001849 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001850}
1851
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001852GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001853 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001854 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001855}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001856
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001857void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1858 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001859 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001860 if (outer.isEmpty()) {
1861 return;
1862 }
1863 if (inner.isEmpty()) {
1864 this->drawRRect(outer, paint);
1865 return;
1866 }
1867
1868 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001869 // be able to return ...
1870 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001871 //
1872 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001873 if (!outer.getBounds().contains(inner.getBounds())) {
1874 return;
1875 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001876
1877 this->onDrawDRRect(outer, inner, paint);
1878}
1879
reed41af9662015-01-05 07:49:08 -08001880void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001881 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001882 this->onDrawPaint(paint);
1883}
1884
1885void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001886 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001887 // To avoid redundant logic in our culling code and various backends, we always sort rects
1888 // before passing them along.
1889 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001890}
1891
Mike Reedd5674082019-04-19 15:00:47 -04001892void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1893 TRACE_EVENT0("skia", TRACE_FUNC);
1894 this->onDrawBehind(paint);
1895}
1896
msarettdca352e2016-08-26 06:37:45 -07001897void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001898 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001899 if (region.isEmpty()) {
1900 return;
1901 }
1902
1903 if (region.isRect()) {
1904 return this->drawIRect(region.getBounds(), paint);
1905 }
1906
1907 this->onDrawRegion(region, paint);
1908}
1909
reed41af9662015-01-05 07:49:08 -08001910void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001911 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001912 // To avoid redundant logic in our culling code and various backends, we always sort rects
1913 // before passing them along.
1914 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001915}
1916
1917void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001918 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001919 this->onDrawRRect(rrect, paint);
1920}
1921
1922void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001923 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001924 this->onDrawPoints(mode, count, pts, paint);
1925}
1926
Mike Reede88a1cb2017-03-17 09:50:46 -04001927void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1928 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001929 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001930 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001931 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1932 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001933 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001934}
1935
1936void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001937 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001938 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001939 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1940}
1941
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001942void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1943 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001944 TRACE_EVENT0("skia", TRACE_FUNC);
1945 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001946 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001947 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1948}
1949
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001950void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1951 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001952 TRACE_EVENT0("skia", TRACE_FUNC);
1953 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001954 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001955 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001956}
1957
1958void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001959 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001960 this->onDrawPath(path, paint);
1961}
1962
reeda85d4d02015-05-06 12:56:48 -07001963void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001964 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001965 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001966 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001967}
1968
Mike Reedc4e31092018-01-30 11:15:27 -05001969// Returns true if the rect can be "filled" : non-empty and finite
1970static bool fillable(const SkRect& r) {
1971 SkScalar w = r.width();
1972 SkScalar h = r.height();
1973 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1974}
1975
reede47829b2015-08-06 10:02:53 -07001976void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1977 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001978 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001979 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001980 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001981 return;
1982 }
1983 this->onDrawImageRect(image, &src, dst, paint, constraint);
1984}
reed41af9662015-01-05 07:49:08 -08001985
reed84984ef2015-07-17 07:09:43 -07001986void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1987 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001988 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001989 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001990}
1991
Brian Salomonf08002c2018-10-26 16:15:46 -04001992void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001993 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001994 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001995 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001996}
reede47829b2015-08-06 10:02:53 -07001997
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001998namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001999class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002000public:
Brian Salomon969be1c2018-05-21 14:37:49 -04002001 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
2002 if (!origPaint) {
2003 return;
2004 }
2005 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
2006 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
2007 }
2008 if (origPaint->getMaskFilter()) {
2009 fPaint.writable()->setMaskFilter(nullptr);
2010 }
2011 if (origPaint->isAntiAlias()) {
2012 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002013 }
2014 }
2015
2016 const SkPaint* get() const {
2017 return fPaint;
2018 }
2019
2020private:
Brian Salomon969be1c2018-05-21 14:37:49 -04002021 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04002022};
2023} // namespace
2024
reed4c21dc52015-06-25 12:32:03 -07002025void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2026 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002027 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002028 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002029 if (dst.isEmpty()) {
2030 return;
2031 }
msarett552bca92016-08-03 06:53:26 -07002032 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002033 LatticePaint latticePaint(paint);
2034 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002035 } else {
reede47829b2015-08-06 10:02:53 -07002036 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002037 }
reed4c21dc52015-06-25 12:32:03 -07002038}
2039
msarett16882062016-08-16 09:31:08 -07002040void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2041 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002042 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002043 RETURN_ON_NULL(image);
2044 if (dst.isEmpty()) {
2045 return;
2046 }
msarett71df2d72016-09-30 12:41:42 -07002047
2048 SkIRect bounds;
2049 Lattice latticePlusBounds = lattice;
2050 if (!latticePlusBounds.fBounds) {
2051 bounds = SkIRect::MakeWH(image->width(), image->height());
2052 latticePlusBounds.fBounds = &bounds;
2053 }
2054
2055 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002056 LatticePaint latticePaint(paint);
2057 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07002058 } else {
2059 this->drawImageRect(image, dst, paint);
2060 }
2061}
2062
reed41af9662015-01-05 07:49:08 -08002063void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002064 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002065 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002066 return;
2067 }
reed41af9662015-01-05 07:49:08 -08002068 this->onDrawBitmap(bitmap, dx, dy, paint);
2069}
2070
reede47829b2015-08-06 10:02:53 -07002071void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002072 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002073 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07002074 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002075 return;
2076 }
reede47829b2015-08-06 10:02:53 -07002077 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002078}
2079
reed84984ef2015-07-17 07:09:43 -07002080void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2081 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002082 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002083}
2084
reede47829b2015-08-06 10:02:53 -07002085void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2086 SrcRectConstraint constraint) {
2087 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2088 constraint);
2089}
reede47829b2015-08-06 10:02:53 -07002090
reed41af9662015-01-05 07:49:08 -08002091void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2092 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002093 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002094 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002095 return;
2096 }
msarett552bca92016-08-03 06:53:26 -07002097 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002098 LatticePaint latticePaint(paint);
2099 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002100 } else {
reeda5517e22015-07-14 10:54:12 -07002101 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002102 }
reed41af9662015-01-05 07:49:08 -08002103}
2104
msarettc573a402016-08-02 08:05:56 -07002105void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2106 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002107 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002108 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002109 return;
2110 }
msarett71df2d72016-09-30 12:41:42 -07002111
2112 SkIRect bounds;
2113 Lattice latticePlusBounds = lattice;
2114 if (!latticePlusBounds.fBounds) {
2115 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2116 latticePlusBounds.fBounds = &bounds;
2117 }
2118
2119 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002120 LatticePaint latticePaint(paint);
2121 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002122 } else {
msarett16882062016-08-16 09:31:08 -07002123 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002124 }
msarettc573a402016-08-02 08:05:56 -07002125}
2126
reed71c3c762015-06-24 10:29:17 -07002127void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002128 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002129 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002130 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002131 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002132 if (count <= 0) {
2133 return;
2134 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002135 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002136 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002137 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002138}
2139
reedf70b5312016-03-04 16:36:20 -08002140void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002141 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002142 if (key) {
2143 this->onDrawAnnotation(rect, key, value);
2144 }
2145}
2146
reede47829b2015-08-06 10:02:53 -07002147void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2148 const SkPaint* paint, SrcRectConstraint constraint) {
2149 if (src) {
2150 this->drawImageRect(image, *src, dst, paint, constraint);
2151 } else {
2152 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2153 dst, paint, constraint);
2154 }
2155}
2156void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2157 const SkPaint* paint, SrcRectConstraint constraint) {
2158 if (src) {
2159 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2160 } else {
2161 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2162 dst, paint, constraint);
2163 }
2164}
2165
Mike Reed4204da22017-05-17 08:53:36 -04002166void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002167 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002168 this->onDrawShadowRec(path, rec);
2169}
2170
2171void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2172 SkPaint paint;
2173 const SkRect& pathBounds = path.getBounds();
2174
Mike Reed38992392019-07-30 10:48:15 -04002175 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002176 while (iter.next()) {
2177 iter.fDevice->drawShadow(path, rec);
2178 }
Mike Reed38992392019-07-30 10:48:15 -04002179 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002180}
2181
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002182void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002183 QuadAAFlags aaFlags, const SkColor4f& color,
2184 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002185 TRACE_EVENT0("skia", TRACE_FUNC);
2186 // Make sure the rect is sorted before passing it along
2187 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2188}
2189
2190void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2191 const SkPoint dstClips[],
2192 const SkMatrix preViewMatrices[],
2193 const SkPaint* paint,
2194 SrcRectConstraint constraint) {
2195 TRACE_EVENT0("skia", TRACE_FUNC);
2196 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2197}
2198
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199//////////////////////////////////////////////////////////////////////////////
2200// These are the virtual drawing methods
2201//////////////////////////////////////////////////////////////////////////////
2202
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002203void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002204 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002205 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2206 }
2207}
2208
reed41af9662015-01-05 07:49:08 -08002209void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002210 this->internalDrawPaint(paint);
2211}
2212
2213void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002214 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215
2216 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002217 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218 }
2219
Mike Reed38992392019-07-30 10:48:15 -04002220 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221}
2222
reed41af9662015-01-05 07:49:08 -08002223void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2224 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225 if ((long)count <= 0) {
2226 return;
2227 }
2228
Mike Reed822128b2017-02-28 16:41:03 -05002229 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002230 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002231 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002232 // special-case 2 points (common for drawing a single line)
2233 if (2 == count) {
2234 r.set(pts[0], pts[1]);
2235 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002236 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002237 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002238 if (!r.isFinite()) {
2239 return;
2240 }
Mike Reed822128b2017-02-28 16:41:03 -05002241 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002242 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2243 return;
2244 }
2245 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002246 }
reed@google.coma584aed2012-05-16 14:06:02 +00002247
halcanary96fcdcc2015-08-27 07:41:13 -07002248 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249
Mike Reed38992392019-07-30 10:48:15 -04002250 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002251
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002253 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254 }
reed@google.com4b226022011-01-11 18:32:13 +00002255
Mike Reed38992392019-07-30 10:48:15 -04002256 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257}
2258
reed4a167172016-08-18 17:15:25 -07002259static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002260 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002261}
2262
reed41af9662015-01-05 07:49:08 -08002263void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002264 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002266 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002267 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002268 return;
2269 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 }
reed@google.com4b226022011-01-11 18:32:13 +00002271
reed4a167172016-08-18 17:15:25 -07002272 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002273 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274
reed4a167172016-08-18 17:15:25 -07002275 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002276 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002277 }
2278
Mike Reed38992392019-07-30 10:48:15 -04002279 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002280 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002281 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002282 SkDrawIter iter(this);
2283 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002284 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002285 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287}
2288
msarett44df6512016-08-25 13:54:30 -07002289void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002290 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002291 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002292 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002293 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2294 return;
2295 }
msarett44df6512016-08-25 13:54:30 -07002296 }
2297
Mike Reed38992392019-07-30 10:48:15 -04002298 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002299
2300 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002301 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002302 }
2303
Mike Reed38992392019-07-30 10:48:15 -04002304 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002305}
2306
Mike Reedd5674082019-04-19 15:00:47 -04002307void SkCanvas::onDrawBehind(const SkPaint& paint) {
2308 SkIRect bounds;
2309 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2310 for (;;) {
2311 const MCRec* rec = (const MCRec*)iter.prev();
2312 if (!rec) {
2313 return; // no backimages, so nothing to draw
2314 }
2315 if (rec->fBackImage) {
2316 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2317 rec->fBackImage->fImage->width(),
2318 rec->fBackImage->fImage->height());
2319 break;
2320 }
2321 }
2322
Mike Reed38992392019-07-30 10:48:15 -04002323 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002324
2325 while (iter.next()) {
2326 SkBaseDevice* dev = iter.fDevice;
2327
Mike Reedd5674082019-04-19 15:00:47 -04002328 dev->save();
2329 // We use clipRegion because it is already defined to operate in dev-space
2330 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2331 // but we don't want that, so we undo that before calling in.
Michael Ludwig915b7792019-10-22 17:40:41 +00002332 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002333 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002334 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002335 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002336 }
2337
Mike Reed38992392019-07-30 10:48:15 -04002338 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002339}
2340
reed41af9662015-01-05 07:49:08 -08002341void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002342 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002343 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002344 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002345 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002346 return;
2347 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002348 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002349
Mike Reed38992392019-07-30 10:48:15 -04002350 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002351
2352 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002353 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002354 }
2355
Mike Reed38992392019-07-30 10:48:15 -04002356 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002357}
2358
bsalomonac3aa242016-08-19 11:25:19 -07002359void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2360 SkScalar sweepAngle, bool useCenter,
2361 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002362 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002363 if (paint.canComputeFastBounds()) {
2364 SkRect storage;
2365 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002366 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002367 return;
2368 }
bsalomonac3aa242016-08-19 11:25:19 -07002369 }
2370
Mike Reed38992392019-07-30 10:48:15 -04002371 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002372
2373 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002374 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002375 }
2376
Mike Reed38992392019-07-30 10:48:15 -04002377 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002378}
2379
reed41af9662015-01-05 07:49:08 -08002380void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002381 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002382 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002383 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2384 return;
2385 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002386 }
2387
2388 if (rrect.isRect()) {
2389 // call the non-virtual version
2390 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002391 return;
2392 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002393 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002394 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2395 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002396 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002397
Mike Reed38992392019-07-30 10:48:15 -04002398 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002399
2400 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002401 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002402 }
2403
Mike Reed38992392019-07-30 10:48:15 -04002404 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002405}
2406
Mike Reed822128b2017-02-28 16:41:03 -05002407void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002408 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002409 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002410 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2411 return;
2412 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002413 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002414
Mike Reed38992392019-07-30 10:48:15 -04002415 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002416
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002417 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002418 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002419 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002420
Mike Reed38992392019-07-30 10:48:15 -04002421 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002422}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002423
reed41af9662015-01-05 07:49:08 -08002424void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002425 if (!path.isFinite()) {
2426 return;
2427 }
2428
Mike Reed822128b2017-02-28 16:41:03 -05002429 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002430 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002431 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002432 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2433 return;
2434 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002436
Mike Reed822128b2017-02-28 16:41:03 -05002437 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002438 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002439 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002440 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002441 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002443
Mike Reed38992392019-07-30 10:48:15 -04002444 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002445
2446 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002447 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448 }
2449
Mike Reed38992392019-07-30 10:48:15 -04002450 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451}
2452
reed262a71b2015-12-05 13:07:27 -08002453bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002454 if (!paint.getImageFilter()) {
2455 return false;
2456 }
2457
2458 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002459 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002460 return false;
2461 }
2462
2463 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2464 // Once we can filter and the filter will return a result larger than itself, we should be
2465 // able to remove this constraint.
2466 // skbug.com/4526
2467 //
2468 SkPoint pt;
2469 ctm.mapXY(x, y, &pt);
2470 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2471 return ir.contains(fMCRec->fRasterClip.getBounds());
2472}
2473
Mike Reedf441cfc2018-04-11 14:50:16 -04002474// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2475// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2476// null.
2477static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2478 if (paintParam) {
2479 *real = *paintParam;
2480 real->setStyle(SkPaint::kFill_Style);
2481 real->setPathEffect(nullptr);
2482 paintParam = real;
2483 }
2484 return paintParam;
2485}
2486
reeda85d4d02015-05-06 12:56:48 -07002487void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002488 SkPaint realPaint;
2489 paint = init_image_paint(&realPaint, paint);
2490
reeda85d4d02015-05-06 12:56:48 -07002491 SkRect bounds = SkRect::MakeXYWH(x, y,
2492 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002493 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002494 SkRect tmp = bounds;
2495 if (paint) {
2496 paint->computeFastBounds(tmp, &tmp);
2497 }
2498 if (this->quickReject(tmp)) {
2499 return;
2500 }
reeda85d4d02015-05-06 12:56:48 -07002501 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002502 // At this point we need a real paint object. If the caller passed null, then we should
2503 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2504 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2505 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002506
reeda2217ef2016-07-20 06:04:34 -07002507 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002508 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2509 *paint);
2510 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002511 special = this->getDevice()->makeSpecial(image);
2512 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002513 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002514 }
2515 }
2516
Mike Reed38992392019-07-30 10:48:15 -04002517 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002518
reeda85d4d02015-05-06 12:56:48 -07002519 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002520 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002521 if (special) {
2522 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002523 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002524 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002525 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002526 SkScalarRoundToInt(pt.fY), pnt,
2527 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002528 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002529 iter.fDevice->drawImageRect(
2530 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2531 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002532 }
reeda85d4d02015-05-06 12:56:48 -07002533 }
halcanary9d524f22016-03-29 09:03:52 -07002534
Mike Reed38992392019-07-30 10:48:15 -04002535 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002536}
2537
reed41af9662015-01-05 07:49:08 -08002538void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002539 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002540 SkPaint realPaint;
2541 paint = init_image_paint(&realPaint, paint);
2542
halcanary96fcdcc2015-08-27 07:41:13 -07002543 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002544 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002545 if (paint) {
2546 paint->computeFastBounds(dst, &storage);
2547 }
2548 if (this->quickReject(storage)) {
2549 return;
2550 }
reeda85d4d02015-05-06 12:56:48 -07002551 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002552 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002553
Mike Reed38992392019-07-30 10:48:15 -04002554 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002555
reeda85d4d02015-05-06 12:56:48 -07002556 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002557 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002558 }
halcanary9d524f22016-03-29 09:03:52 -07002559
Mike Reed38992392019-07-30 10:48:15 -04002560 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002561}
2562
reed41af9662015-01-05 07:49:08 -08002563void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564 SkDEBUGCODE(bitmap.validate();)
2565
reed33366972015-10-08 09:22:02 -07002566 if (bitmap.drawsNothing()) {
2567 return;
2568 }
2569
Mike Reedf441cfc2018-04-11 14:50:16 -04002570 SkPaint realPaint;
2571 init_image_paint(&realPaint, paint);
2572 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002573
Mike Reed822128b2017-02-28 16:41:03 -05002574 SkRect bounds;
2575 bitmap.getBounds(&bounds);
2576 bounds.offset(x, y);
2577 bool canFastBounds = paint->canComputeFastBounds();
2578 if (canFastBounds) {
2579 SkRect storage;
2580 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002581 return;
2582 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002583 }
reed@google.com4b226022011-01-11 18:32:13 +00002584
reeda2217ef2016-07-20 06:04:34 -07002585 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002586 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2587 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002588 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002589 special = this->getDevice()->makeSpecial(bitmap);
2590 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002591 drawAsSprite = false;
2592 }
2593 }
2594
Mike Reed38992392019-07-30 10:48:15 -04002595 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002596
2597 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002598 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002599 if (special) {
reed262a71b2015-12-05 13:07:27 -08002600 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002601 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002602 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002603 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002604 SkScalarRoundToInt(pt.fY), pnt,
2605 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002606 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002607 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2608 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2609 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002610 }
reed33366972015-10-08 09:22:02 -07002611 }
msarettfbfa2582016-08-12 08:29:08 -07002612
Mike Reed38992392019-07-30 10:48:15 -04002613 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614}
2615
reed@google.com9987ec32011-09-07 11:57:52 +00002616// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002617void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002618 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002619 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002620 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002621 return;
2622 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002623
halcanary96fcdcc2015-08-27 07:41:13 -07002624 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002625 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002626 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2627 return;
2628 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002629 }
reed@google.com3d608122011-11-21 15:16:16 +00002630
reed@google.com33535f32012-09-25 15:37:50 +00002631 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002632 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002633 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002634 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002635
Mike Reed38992392019-07-30 10:48:15 -04002636 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002637
reed@google.com33535f32012-09-25 15:37:50 +00002638 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002639 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002640 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002641
Mike Reed38992392019-07-30 10:48:15 -04002642 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002643}
2644
reed41af9662015-01-05 07:49:08 -08002645void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002646 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002647 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002648 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002649}
2650
reed4c21dc52015-06-25 12:32:03 -07002651void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2652 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002653 SkPaint realPaint;
2654 paint = init_image_paint(&realPaint, paint);
2655
halcanary96fcdcc2015-08-27 07:41:13 -07002656 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002657 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002658 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2659 return;
2660 }
reed@google.com3d608122011-11-21 15:16:16 +00002661 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002662 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002663
Mike Reed38992392019-07-30 10:48:15 -04002664 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002665
reed4c21dc52015-06-25 12:32:03 -07002666 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002667 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002668 }
halcanary9d524f22016-03-29 09:03:52 -07002669
Mike Reed38992392019-07-30 10:48:15 -04002670 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002671}
2672
reed41af9662015-01-05 07:49:08 -08002673void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2674 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002675 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002676 SkPaint realPaint;
2677 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002678
halcanary96fcdcc2015-08-27 07:41:13 -07002679 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002680 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002681 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2682 return;
2683 }
reed4c21dc52015-06-25 12:32:03 -07002684 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002685 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002686
Mike Reed38992392019-07-30 10:48:15 -04002687 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002688
reed4c21dc52015-06-25 12:32:03 -07002689 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002690 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002691 }
halcanary9d524f22016-03-29 09:03:52 -07002692
Mike Reed38992392019-07-30 10:48:15 -04002693 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002694}
2695
msarett16882062016-08-16 09:31:08 -07002696void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2697 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002698 SkPaint realPaint;
2699 paint = init_image_paint(&realPaint, paint);
2700
msarett16882062016-08-16 09:31:08 -07002701 if (nullptr == paint || paint->canComputeFastBounds()) {
2702 SkRect storage;
2703 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2704 return;
2705 }
2706 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002707 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002708
Mike Reed38992392019-07-30 10:48:15 -04002709 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002710
2711 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002712 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002713 }
2714
Mike Reed38992392019-07-30 10:48:15 -04002715 DRAW_END
msarett16882062016-08-16 09:31:08 -07002716}
2717
2718void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2719 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002720 SkPaint realPaint;
2721 paint = init_image_paint(&realPaint, paint);
2722
msarett16882062016-08-16 09:31:08 -07002723 if (nullptr == paint || paint->canComputeFastBounds()) {
2724 SkRect storage;
2725 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2726 return;
2727 }
2728 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002729 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002730
Mike Reed38992392019-07-30 10:48:15 -04002731 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002732
2733 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002734 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002735 }
2736
Mike Reed38992392019-07-30 10:48:15 -04002737 DRAW_END
msarett16882062016-08-16 09:31:08 -07002738}
2739
fmalita00d5c2c2014-08-21 08:53:26 -07002740void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2741 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002742 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002743 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002744 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002745 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002746 SkRect tmp;
2747 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2748 return;
2749 }
2750 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002751 }
2752
fmalita024f9962015-03-03 19:08:17 -08002753 // We cannot filter in the looper as we normally do, because the paint is
2754 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002755 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002756
fmalitaaa1b9122014-08-28 14:32:24 -07002757 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002758 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002759 }
2760
Mike Reed38992392019-07-30 10:48:15 -04002761 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002762}
2763
Mike Reed358fcad2018-11-23 15:27:51 -05002764// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002765void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002766 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2767 TRACE_EVENT0("skia", TRACE_FUNC);
2768 if (byteLength) {
2769 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002770 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002771 }
2772}
Mike Reed4f81bb72019-01-23 09:23:00 -05002773
fmalita00d5c2c2014-08-21 08:53:26 -07002774void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2775 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002776 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002777 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002778 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002779 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002780}
reed@google.come0d9ce82014-04-23 04:00:17 +00002781
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002782void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002783 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002784 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002785
2786 while (iter.next()) {
2787 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002788 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002789 }
2790
Mike Reed38992392019-07-30 10:48:15 -04002791 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002792}
2793
dandovb3c9d1c2014-08-12 08:34:29 -07002794void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002795 const SkPoint texCoords[4], SkBlendMode bmode,
2796 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002797 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002798 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002799 return;
2800 }
mtklein6cfa73a2014-08-13 13:33:49 -07002801
Mike Reedfaba3712016-11-03 14:45:31 -04002802 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002803}
2804
2805void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002806 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002807 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002808 // Since a patch is always within the convex hull of the control points, we discard it when its
2809 // bounding rectangle is completely outside the current clip.
2810 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002811 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002812 if (this->quickReject(bounds)) {
2813 return;
2814 }
mtklein6cfa73a2014-08-13 13:33:49 -07002815
Mike Reed38992392019-07-30 10:48:15 -04002816 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002817
dandovecfff212014-08-04 10:02:00 -07002818 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002819 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002820 }
mtklein6cfa73a2014-08-13 13:33:49 -07002821
Mike Reed38992392019-07-30 10:48:15 -04002822 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002823}
2824
reeda8db7282015-07-07 10:22:31 -07002825void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002826#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002827 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002828#endif
reede3b38ce2016-01-08 09:18:44 -08002829 RETURN_ON_NULL(dr);
2830 if (x || y) {
2831 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2832 this->onDrawDrawable(dr, &matrix);
2833 } else {
2834 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002835 }
2836}
2837
reeda8db7282015-07-07 10:22:31 -07002838void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002839#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002840 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002841#endif
reede3b38ce2016-01-08 09:18:44 -08002842 RETURN_ON_NULL(dr);
2843 if (matrix && matrix->isIdentity()) {
2844 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002845 }
reede3b38ce2016-01-08 09:18:44 -08002846 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002847}
2848
2849void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002850 // drawable bounds are no longer reliable (e.g. android displaylist)
2851 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002852 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002853}
2854
reed71c3c762015-06-24 10:29:17 -07002855void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002856 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002857 const SkRect* cull, const SkPaint* paint) {
2858 if (cull && this->quickReject(*cull)) {
2859 return;
2860 }
2861
2862 SkPaint pnt;
2863 if (paint) {
2864 pnt = *paint;
2865 }
halcanary9d524f22016-03-29 09:03:52 -07002866
Mike Reed38992392019-07-30 10:48:15 -04002867 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002868 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002869 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002870 }
Mike Reed38992392019-07-30 10:48:15 -04002871 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002872}
2873
reedf70b5312016-03-04 16:36:20 -08002874void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2875 SkASSERT(key);
2876
2877 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002878 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002879 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002880 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002881 }
Mike Reed38992392019-07-30 10:48:15 -04002882 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002883}
2884
Michael Ludwiga595f862019-08-27 15:25:49 -04002885void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2886 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002887 SkASSERT(r.isSorted());
2888
2889 // If this used a paint, it would be a filled color with blend mode, which does not
2890 // need to use an autodraw loop, so use SkDrawIter directly.
2891 if (this->quickReject(r)) {
2892 return;
2893 }
2894
Michael Ludwiga4b44882019-08-28 14:34:58 -04002895 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002896 SkDrawIter iter(this);
2897 while(iter.next()) {
2898 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2899 }
2900}
2901
2902void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2903 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2904 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002905 if (count <= 0) {
2906 // Nothing to draw
2907 return;
2908 }
2909
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002910 SkPaint realPaint;
2911 init_image_paint(&realPaint, paint);
2912
Michael Ludwiga4b44882019-08-28 14:34:58 -04002913 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2914 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2915 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2916 // or we need it for the autolooper (since it greatly improves image filter perf).
2917 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2918 bool setBoundsValid = count == 1 || needsAutoLooper;
2919 SkRect setBounds = imageSet[0].fDstRect;
2920 if (imageSet[0].fMatrixIndex >= 0) {
2921 // Account for the per-entry transform that is applied prior to the CTM when drawing
2922 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002923 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002924 if (needsAutoLooper) {
2925 for (int i = 1; i < count; ++i) {
2926 SkRect entryBounds = imageSet[i].fDstRect;
2927 if (imageSet[i].fMatrixIndex >= 0) {
2928 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2929 }
2930 setBounds.joinPossiblyEmptyRect(entryBounds);
2931 }
2932 }
2933
2934 // If we happen to have the draw bounds, though, might as well check quickReject().
2935 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2936 SkRect tmp;
2937 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2938 return;
2939 }
2940 }
2941
2942 if (needsAutoLooper) {
2943 SkASSERT(setBoundsValid);
2944 DRAW_BEGIN(realPaint, &setBounds)
2945 while (iter.next()) {
2946 iter.fDevice->drawEdgeAAImageSet(
2947 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2948 }
2949 DRAW_END
2950 } else {
2951 this->predrawNotify();
2952 SkDrawIter iter(this);
2953 while(iter.next()) {
2954 iter.fDevice->drawEdgeAAImageSet(
2955 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2956 }
2957 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002958}
2959
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960//////////////////////////////////////////////////////////////////////////////
2961// These methods are NOT virtual, and therefore must call back into virtual
2962// methods, rather than actually drawing themselves.
2963//////////////////////////////////////////////////////////////////////////////
2964
reed374772b2016-10-05 17:33:02 -07002965void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002966 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002967 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002968 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002969 this->drawPaint(paint);
2970}
2971
2972void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002973 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002974 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2975}
2976
Mike Reed3661bc92017-02-22 13:21:42 -05002977void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002978 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002979 pts[0].set(x0, y0);
2980 pts[1].set(x1, y1);
2981 this->drawPoints(kLines_PointMode, 2, pts, paint);
2982}
2983
Mike Reed3661bc92017-02-22 13:21:42 -05002984void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002985 if (radius < 0) {
2986 radius = 0;
2987 }
2988
2989 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002990 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002991 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002992}
2993
2994void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2995 const SkPaint& paint) {
2996 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002997 SkRRect rrect;
2998 rrect.setRectXY(r, rx, ry);
2999 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000 } else {
3001 this->drawRect(r, paint);
3002 }
3003}
3004
reed@android.com8a1c16f2008-12-17 15:59:43 +00003005void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3006 SkScalar sweepAngle, bool useCenter,
3007 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04003008 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07003009 if (oval.isEmpty() || !sweepAngle) {
3010 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003011 }
bsalomon21af9ca2016-08-25 12:29:23 -07003012 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003013}
3014
reed@android.comf76bacf2009-05-13 14:00:33 +00003015///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003016#ifdef SK_DISABLE_SKPICTURE
3017void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07003018
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003019
3020void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3021 const SkPaint* paint) {}
3022#else
Mike Klein88d90712018-01-27 17:30:04 +00003023/**
3024 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3025 * against the playback cost of recursing into the subpicture to get at its actual ops.
3026 *
3027 * For now we pick a conservatively small value, though measurement (and other heuristics like
3028 * the type of ops contained) may justify changing this value.
3029 */
3030#define kMaxPictureOpsToUnrollInsteadOfRef 1
3031
reedd5fa1a42014-08-09 11:08:05 -07003032void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04003033 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08003034 RETURN_ON_NULL(picture);
3035
reede3b38ce2016-01-08 09:18:44 -08003036 if (matrix && matrix->isIdentity()) {
3037 matrix = nullptr;
3038 }
Mike Klein88d90712018-01-27 17:30:04 +00003039 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3040 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3041 picture->playback(this);
3042 } else {
3043 this->onDrawPicture(picture, matrix, paint);
3044 }
reedd5fa1a42014-08-09 11:08:05 -07003045}
robertphillips9b14f262014-06-04 05:40:44 -07003046
reedd5fa1a42014-08-09 11:08:05 -07003047void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3048 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003049 if (!paint || paint->canComputeFastBounds()) {
3050 SkRect bounds = picture->cullRect();
3051 if (paint) {
3052 paint->computeFastBounds(bounds, &bounds);
3053 }
3054 if (matrix) {
3055 matrix->mapRect(&bounds);
3056 }
3057 if (this->quickReject(bounds)) {
3058 return;
3059 }
3060 }
3061
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003062 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003063 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003064}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04003065#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00003066
reed@android.com8a1c16f2008-12-17 15:59:43 +00003067///////////////////////////////////////////////////////////////////////////////
3068///////////////////////////////////////////////////////////////////////////////
3069
reed3aafe112016-08-18 12:45:34 -07003070SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003071 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003072
3073 SkASSERT(canvas);
3074
reed3aafe112016-08-18 12:45:34 -07003075 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003076 fDone = !fImpl->next();
3077}
3078
3079SkCanvas::LayerIter::~LayerIter() {
3080 fImpl->~SkDrawIter();
3081}
3082
3083void SkCanvas::LayerIter::next() {
3084 fDone = !fImpl->next();
3085}
3086
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003087SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003088 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003089}
3090
3091const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003092 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003093}
3094
3095const SkPaint& SkCanvas::LayerIter::paint() const {
3096 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003097 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003098 paint = &fDefaultPaint;
3099 }
3100 return *paint;
3101}
3102
Mike Reedca37f322018-03-08 13:22:16 -05003103SkIRect SkCanvas::LayerIter::clipBounds() const {
3104 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003105}
3106
Michael Ludwig915b7792019-10-22 17:40:41 +00003107int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3108int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003109
3110///////////////////////////////////////////////////////////////////////////////
3111
Brian Osmane8a98632019-04-10 10:26:10 -04003112SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3113SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3114SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3115SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3116
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003117SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3118 const SkRect& dstRect, int matrixIndex, float alpha,
3119 unsigned aaFlags, bool hasClip)
3120 : fImage(std::move(image))
3121 , fSrcRect(srcRect)
3122 , fDstRect(dstRect)
3123 , fMatrixIndex(matrixIndex)
3124 , fAlpha(alpha)
3125 , fAAFlags(aaFlags)
3126 , fHasClip(hasClip) {}
3127
3128SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3129 const SkRect& dstRect, float alpha, unsigned aaFlags)
3130 : fImage(std::move(image))
3131 , fSrcRect(srcRect)
3132 , fDstRect(dstRect)
3133 , fAlpha(alpha)
3134 , fAAFlags(aaFlags) {}
3135
3136///////////////////////////////////////////////////////////////////////////////
3137
Mike Reed5df49342016-11-12 08:06:55 -06003138std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003139 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003140 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003141 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003142 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003143
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003144 SkBitmap bitmap;
3145 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003146 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003147 }
Mike Reed12f77342017-11-08 11:19:52 -05003148
3149 return props ?
Mike Kleinf46d5ca2019-12-11 10:45:01 -05003150 std::make_unique<SkCanvas>(bitmap, *props) :
3151 std::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003152}
reedd5fa1a42014-08-09 11:08:05 -07003153
3154///////////////////////////////////////////////////////////////////////////////
3155
Florin Malitaee424ac2016-12-01 12:47:59 -05003156SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003157 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003158
Florin Malita439ace92016-12-02 12:05:41 -05003159SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003160 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003161
Herb Derbyefe39bc2018-05-01 17:06:20 -04003162SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003163 : INHERITED(device) {}
3164
Florin Malitaee424ac2016-12-01 12:47:59 -05003165SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3166 (void)this->INHERITED::getSaveLayerStrategy(rec);
3167 return kNoLayer_SaveLayerStrategy;
3168}
3169
Mike Reed148b7fd2018-12-18 17:38:18 -05003170bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3171 return false;
3172}
3173
Florin Malitaee424ac2016-12-01 12:47:59 -05003174///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003175
reed73603f32016-09-20 08:42:38 -07003176static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3177static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3178static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3179static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3180static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3181static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003182
3183///////////////////////////////////////////////////////////////////////////////////////////////////
3184
3185SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3186 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003187 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003188 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwig915b7792019-10-22 17:40:41 +00003189 SkIPoint origin = dev->getOrigin();
3190 SkMatrix ctm = this->getTotalMatrix();
3191 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3192
3193 SkIRect clip = fMCRec->fRasterClip.getBounds();
3194 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003195 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003196 clip.setEmpty();
3197 }
3198
Michael Ludwig915b7792019-10-22 17:40:41 +00003199 fAllocator->updateHandle(handle, ctm, clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003200 return handle;
3201 }
3202 return nullptr;
3203}
3204
3205static bool install(SkBitmap* bm, const SkImageInfo& info,
3206 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003207 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003208}
3209
3210SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3211 SkBitmap* bm) {
3212 SkRasterHandleAllocator::Rec rec;
3213 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3214 return nullptr;
3215 }
3216 return rec.fHandle;
3217}
3218
3219std::unique_ptr<SkCanvas>
3220SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3221 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003222 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003223 return nullptr;
3224 }
3225
3226 SkBitmap bm;
3227 Handle hndl;
3228
3229 if (rec) {
3230 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3231 } else {
3232 hndl = alloc->allocBitmap(info, &bm);
3233 }
3234 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3235}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003236
3237///////////////////////////////////////////////////////////////////////////////////////////////////