blob: cfb126fc3d010e1a559c0492d18042a7c2948514 [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"
Mike Reed55f6fc32020-01-21 12:23:51 -050013#include "include/core/SkMatrix44.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/core/SkPathEffect.h"
15#include "include/core/SkPicture.h"
16#include "include/core/SkRRect.h"
17#include "include/core/SkRasterHandleAllocator.h"
18#include "include/core/SkString.h"
19#include "include/core/SkTextBlob.h"
20#include "include/core/SkVertices.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/private/SkNx.h"
22#include "include/private/SkTo.h"
23#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040024#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/core/SkBitmapDevice.h"
Mike Reed403c8072020-01-08 10:40:39 -050026#include "src/core/SkCanvasMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/core/SkCanvasPriv.h"
28#include "src/core/SkClipOpPriv.h"
29#include "src/core/SkClipStack.h"
30#include "src/core/SkDraw.h"
31#include "src/core/SkGlyphRun.h"
32#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040033#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050034#include "src/core/SkLatticeIter.h"
35#include "src/core/SkMSAN.h"
Mike Reed07d32b42020-01-23 11:06:20 -050036#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "src/core/SkMatrixUtils.h"
38#include "src/core/SkPaintPriv.h"
39#include "src/core/SkRasterClip.h"
40#include "src/core/SkSpecialImage.h"
41#include "src/core/SkStrikeCache.h"
42#include "src/core/SkTLazy.h"
43#include "src/core/SkTextFormatParams.h"
44#include "src/core/SkTraceEvent.h"
45#include "src/image/SkImage_Base.h"
46#include "src/image/SkSurface_Base.h"
47#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040048
bungemand3ebb482015-08-05 13:57:49 -070049#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000050
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000051#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050052#include "include/gpu/GrContext.h"
53#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000054#endif
55
reede3b38ce2016-01-08 09:18:44 -080056#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050057#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080058
Mike Klein1bb7e232019-12-10 08:58:52 -060059// This is a test: static_assert with no message is a c++17 feature,
60// and std::max() is constexpr only since the c++14 stdlib.
61static_assert(std::max(3,4) == 4);
Brian Salomonb0047b52019-12-05 19:52:25 +000062
Mike Reed139e5e02017-03-08 11:29:33 -050063///////////////////////////////////////////////////////////////////////////////////////////////////
64
reedc83a2972015-07-16 07:40:45 -070065/*
66 * Return true if the drawing this rect would hit every pixels in the canvas.
67 *
68 * Returns false if
69 * - rect does not contain the canvas' bounds
70 * - paint is not fill
71 * - paint would blur or otherwise change the coverage of the rect
72 */
73bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
74 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070075 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
76 (int)kNone_ShaderOverrideOpacity,
77 "need_matching_enums0");
78 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
79 (int)kOpaque_ShaderOverrideOpacity,
80 "need_matching_enums1");
81 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
82 (int)kNotOpaque_ShaderOverrideOpacity,
83 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070084
85 const SkISize size = this->getBaseLayerSize();
86 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050087
88 // if we're clipped at all, we can't overwrite the entire surface
89 {
90 SkBaseDevice* base = this->getDevice();
91 SkBaseDevice* top = this->getTopDevice();
92 if (base != top) {
93 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
94 }
95 if (!base->clipIsWideOpen()) {
96 return false;
97 }
reedc83a2972015-07-16 07:40:45 -070098 }
99
100 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -0700101 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -0700102 return false; // conservative
103 }
halcanaryc5769b22016-08-10 07:13:21 -0700104
105 SkRect devRect;
106 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
107 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700108 return false;
109 }
110 }
111
112 if (paint) {
113 SkPaint::Style paintStyle = paint->getStyle();
114 if (!(paintStyle == SkPaint::kFill_Style ||
115 paintStyle == SkPaint::kStrokeAndFill_Style)) {
116 return false;
117 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400118 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700119 return false; // conservative
120 }
121 }
122 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
123}
124
125///////////////////////////////////////////////////////////////////////////////////////////////////
126
reed@google.comda17f752012-08-16 18:27:05 +0000127// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128//#define SK_TRACE_SAVERESTORE
129
130#ifdef SK_TRACE_SAVERESTORE
131 static int gLayerCounter;
132 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
133 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
134
135 static int gRecCounter;
136 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
137 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
138
139 static int gCanvasCounter;
140 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
141 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
142#else
143 #define inc_layer()
144 #define dec_layer()
145 #define inc_rec()
146 #define dec_rec()
147 #define inc_canvas()
148 #define dec_canvas()
149#endif
150
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000151typedef SkTLazy<SkPaint> SkLazyPaint;
152
reedc83a2972015-07-16 07:40:45 -0700153void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000154 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700155 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
156 ? SkSurface::kDiscard_ContentChangeMode
157 : SkSurface::kRetain_ContentChangeMode);
158 }
159}
160
161void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
162 ShaderOverrideOpacity overrideOpacity) {
163 if (fSurfaceBase) {
164 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
165 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
166 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
167 // and therefore we don't care which mode we're in.
168 //
169 if (fSurfaceBase->outstandingImageSnapshot()) {
170 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
171 mode = SkSurface::kDiscard_ContentChangeMode;
172 }
173 }
174 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000175 }
176}
177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000180/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 The clip/matrix/proc are fields that reflect the top of the save/restore
182 stack. Whenever the canvas changes, it marks a dirty flag, and then before
183 these are used (assuming we're not on a layer) we rebuild these cache
184 values: they reflect the top of the save stack, but translated and clipped
185 by the device's XY offset and bitmap-bounds.
186*/
187struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400188 DeviceCM* fNext;
189 sk_sp<SkBaseDevice> fDevice;
190 SkRasterClip fClip;
191 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
192 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400193 sk_sp<SkImage> fClipImage;
194 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195
Florin Malita53f77bd2017-04-28 13:48:37 -0400196 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000197 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700198 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400199 , fDevice(std::move(device))
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500200 , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700201 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000202 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400203 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400204 {}
reed@google.com4b226022011-01-11 18:32:13 +0000205
mtkleinfeaadee2015-04-08 11:25:48 -0700206 void reset(const SkIRect& bounds) {
207 SkASSERT(!fPaint);
208 SkASSERT(!fNext);
209 SkASSERT(fDevice);
210 fClip.setRect(bounds);
211 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212};
213
Mike Reed148b7fd2018-12-18 17:38:18 -0500214namespace {
215// Encapsulate state needed to restore from saveBehind()
216struct BackImage {
217 sk_sp<SkSpecialImage> fImage;
218 SkIPoint fLoc;
219};
220}
221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222/* This is the record we keep for each save/restore level in the stack.
223 Since a level optionally copies the matrix and/or stack, we have pointers
224 for these fields. If the value is copied for this level, the copy is
225 stored in the ...Storage field, and the pointer points to that. If the
226 value is not copied for this level, we ignore ...Storage, and just point
227 at the corresponding value in the previous level in the stack.
228*/
229class SkCanvas::MCRec {
230public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500231 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 /* If there are any layers in the stack, this points to the top-most
233 one that is at or below this level in the stack (so we know what
234 bitmap/device to draw into from this level. This value is NOT
235 reference counted, since the real owner is either our fLayer field,
236 or a previous one in a lower level.)
237 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500238 DeviceCM* fTopLayer;
239 std::unique_ptr<BackImage> fBackImage;
240 SkConservativeClip fRasterClip;
Mike Reed403c8072020-01-08 10:40:39 -0500241 SkCanvasMatrix fMatrix;
Mike Reed148b7fd2018-12-18 17:38:18 -0500242 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
Mike Reeda1361362017-03-07 09:37:29 -0500244 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700245 fLayer = nullptr;
246 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800247 fMatrix.reset();
248 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700249
reedd9544982014-09-09 18:46:22 -0700250 // don't bother initializing fNext
251 inc_rec();
252 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400253 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700254 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700255 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800256 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 // don't bother initializing fNext
259 inc_rec();
260 }
261 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700262 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 dec_rec();
264 }
mtkleinfeaadee2015-04-08 11:25:48 -0700265
266 void reset(const SkIRect& bounds) {
267 SkASSERT(fLayer);
268 SkASSERT(fDeferredSaveCount == 0);
269
270 fMatrix.reset();
271 fRasterClip.setRect(bounds);
272 fLayer->reset(bounds);
273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274};
275
Mike Reeda1361362017-03-07 09:37:29 -0500276class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277public:
Mike Reeda1361362017-03-07 09:37:29 -0500278 SkDrawIter(SkCanvas* canvas)
279 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
280 {}
reed@google.com4b226022011-01-11 18:32:13 +0000281
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000283 const DeviceCM* rec = fCurrLayer;
284 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400285 fDevice = rec->fDevice.get();
286 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700288 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 return true;
290 }
291 return false;
292 }
reed@google.com4b226022011-01-11 18:32:13 +0000293
Michael Ludwig915b7792019-10-22 17:40:41 +0000294 int getX() const { return fDevice->getOrigin().x(); }
295 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000297
Mike Reed99330ba2017-02-22 11:01:08 -0500298 SkBaseDevice* fDevice;
299
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 const DeviceCM* fCurrLayer;
302 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303};
304
Florin Malita713b8ef2017-04-28 10:57:24 -0400305#define FOR_EACH_TOP_DEVICE( code ) \
306 do { \
307 DeviceCM* layer = fMCRec->fTopLayer; \
308 while (layer) { \
309 SkBaseDevice* device = layer->fDevice.get(); \
310 if (device) { \
311 code; \
312 } \
313 layer = layer->fNext; \
314 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500315 } while (0)
316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317/////////////////////////////////////////////////////////////////////////////
318
reeddbc3cef2015-04-29 12:18:57 -0700319/**
320 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700321 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700322 */
reedd053ce92016-03-22 10:17:23 -0700323static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700324 SkImageFilter* imgf = paint.getImageFilter();
325 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700326 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700327 }
328
reedd053ce92016-03-22 10:17:23 -0700329 SkColorFilter* imgCFPtr;
330 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700331 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700332 }
reedd053ce92016-03-22 10:17:23 -0700333 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700334
335 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700336 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700337 // there is no existing paint colorfilter, so we can just return the imagefilter's
338 return imgCF;
339 }
340
341 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
342 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500343 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700344}
345
senorblanco87e066e2015-10-28 11:23:36 -0700346/**
347 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
348 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
349 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
350 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
351 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
352 * conservative "effective" bounds based on the settings in the paint... with one exception. This
353 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
354 * deliberately ignored.
355 */
356static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
357 const SkRect& rawBounds,
358 SkRect* storage) {
359 SkPaint tmpUnfiltered(paint);
360 tmpUnfiltered.setImageFilter(nullptr);
361 if (tmpUnfiltered.canComputeFastBounds()) {
362 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
363 } else {
364 return rawBounds;
365 }
366}
367
Mike Reed38992392019-07-30 10:48:15 -0400368class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369public:
senorblanco87e066e2015-10-28 11:23:36 -0700370 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
371 // paint. It's used to determine the size of the offscreen layer for filters.
372 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400373 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
374 bool skipLayerForImageFilter = false,
375 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400377 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000378 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700379 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380
Mike Reed38992392019-07-30 10:48:15 -0400381 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
382 SkASSERT(!fLazyPaint.isValid());
383 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700384 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700385 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700386 fPaint = paint;
387 }
388
389 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700390 /**
391 * We implement ImageFilters for a given draw by creating a layer, then applying the
392 * imagefilter to the pixels of that layer (its backing surface/image), and then
393 * we call restore() to xfer that layer to the main canvas.
394 *
395 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
396 * 2. Generate the src pixels:
397 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
398 * return (fPaint). We then draw the primitive (using srcover) into a cleared
399 * buffer/surface.
400 * 3. Restore the layer created in #1
401 * The imagefilter is passed the buffer/surface from the layer (now filled with the
402 * src pixels of the primitive). It returns a new "filtered" buffer, which we
403 * draw onto the previous layer using the xfermode from the original paint.
404 */
Mike Reed38992392019-07-30 10:48:15 -0400405
406 SkPaint restorePaint;
407 restorePaint.setImageFilter(fPaint->refImageFilter());
408 restorePaint.setBlendMode(fPaint->getBlendMode());
409
senorblanco87e066e2015-10-28 11:23:36 -0700410 SkRect storage;
411 if (rawBounds) {
412 // Make rawBounds include all paint outsets except for those due to image filters.
413 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
414 }
Mike Reed38992392019-07-30 10:48:15 -0400415 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700416 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700417 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000418
Mike Reed38992392019-07-30 10:48:15 -0400419 // Remove the restorePaint fields from our "working" paint
420 SkASSERT(!fLazyPaint.isValid());
421 SkPaint* paint = fLazyPaint.set(origPaint);
422 paint->setImageFilter(nullptr);
423 paint->setBlendMode(SkBlendMode::kSrcOver);
424 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000425 }
426 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000427
Mike Reed38992392019-07-30 10:48:15 -0400428 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700429 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000430 fCanvas->internalRestore();
431 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000432 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000434
reed@google.com4e2b3d32011-04-07 14:18:59 +0000435 const SkPaint& paint() const {
436 SkASSERT(fPaint);
437 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440private:
Mike Reed38992392019-07-30 10:48:15 -0400441 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000442 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400443 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000444 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700445 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446};
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448////////// macros to place around the internal draw calls //////////////////
449
Mike Reed38992392019-07-30 10:48:15 -0400450#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700451 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400452 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
453 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800454
455
Mike Reed38992392019-07-30 10:48:15 -0400456#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000457 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400458 AutoLayerForImageFilter draw(this, paint, true); \
459 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000460
Mike Reed38992392019-07-30 10:48:15 -0400461#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000462 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400463 AutoLayerForImageFilter draw(this, paint, false, bounds); \
464 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000465
Mike Reed38992392019-07-30 10:48:15 -0400466#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700467 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400468 AutoLayerForImageFilter draw(this, paint, false, bounds); \
469 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700470
Mike Reed38992392019-07-30 10:48:15 -0400471#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472
473////////////////////////////////////////////////////////////////////////////
474
msarettfbfa2582016-08-12 08:29:08 -0700475static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
476 if (bounds.isEmpty()) {
477 return SkRect::MakeEmpty();
478 }
479
480 // Expand bounds out by 1 in case we are anti-aliasing. We store the
481 // bounds as floats to enable a faster quick reject implementation.
482 SkRect dst;
483 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
484 return dst;
485}
486
mtkleinfeaadee2015-04-08 11:25:48 -0700487void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
488 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700489 fMCRec->reset(bounds);
490
491 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500492 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400493 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700494 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700495 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700496}
497
Hal Canary363a3f82018-10-04 11:04:48 -0400498void SkCanvas::init(sk_sp<SkBaseDevice> device) {
reed2ff1fce2014-12-11 07:07:37 -0800499 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500
501 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500502 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500503 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700504 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505
reeda499f902015-05-01 09:34:31 -0700506 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
507 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400508 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511
halcanary96fcdcc2015-08-27 07:41:13 -0700512 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000513
reedf92c8662014-08-18 08:02:43 -0700514 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700515 // The root device and the canvas should always have the same pixel geometry
516 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800517 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700518 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500519
Mike Reedc42a1cd2017-02-14 14:25:14 -0500520 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700521 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400522
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500523 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524}
525
reed@google.comcde92112011-07-06 20:00:52 +0000526SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000527 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700528 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000529{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000530 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000531
Hal Canary363a3f82018-10-04 11:04:48 -0400532 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000533}
534
reed96a857e2015-01-25 10:33:58 -0800535SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000536 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800537 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000538{
539 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400540 this->init(sk_make_sp<SkNoPixelsDevice>(
Hal Canary363a3f82018-10-04 11:04:48 -0400541 SkIRect::MakeWH(SkTMax(width, 0), SkTMax(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700542}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000543
Hal Canary363a3f82018-10-04 11:04:48 -0400544SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700545 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700546 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700547{
548 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700549
Mike Reed566e53c2017-03-10 10:49:45 -0500550 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400551 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700552}
553
Herb Derbyefe39bc2018-05-01 17:06:20 -0400554SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000555 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700556 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000557{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700559
Hal Canary363a3f82018-10-04 11:04:48 -0400560 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700561}
562
reed4a8126e2014-09-22 07:29:03 -0700563SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700564 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700565 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700566{
567 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700568
Mike Reed910ca0f2018-04-25 13:04:05 -0400569 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400570 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700571}
reed29c857d2014-09-21 10:25:07 -0700572
Mike Reed356f7c22017-01-10 11:58:39 -0500573SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
574 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700575 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
576 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500577 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700578{
579 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700580
Mike Reed910ca0f2018-04-25 13:04:05 -0400581 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400582 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583}
584
Mike Reed356f7c22017-01-10 11:58:39 -0500585SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
586
Matt Sarett31f99ce2017-04-11 08:46:01 -0400587#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
588SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
589 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
590 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
591 , fAllocator(nullptr)
592{
593 inc_canvas();
594
595 SkBitmap tmp(bitmap);
596 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400597 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400598 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400599}
600#endif
601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602SkCanvas::~SkCanvas() {
603 // free up the contents of our deque
604 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 this->internalRestore(); // restore the last, since we're going away
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 dec_canvas();
609}
610
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611///////////////////////////////////////////////////////////////////////////////
612
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000613void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700614 this->onFlush();
615}
616
617void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000618 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000619 if (device) {
620 device->flush();
621 }
622}
623
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500624SkSurface* SkCanvas::getSurface() const {
625 return fSurfaceBase;
626}
627
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000628SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000629 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000630 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
631}
632
senorblancoafc7cce2016-02-02 18:44:15 -0800633SkIRect SkCanvas::getTopLayerBounds() const {
634 SkBaseDevice* d = this->getTopDevice();
635 if (!d) {
636 return SkIRect::MakeEmpty();
637 }
Michael Ludwig915b7792019-10-22 17:40:41 +0000638 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
senorblancoafc7cce2016-02-02 18:44:15 -0800639}
640
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000641SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000643 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400645 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646}
647
Florin Malita0ed3b642017-01-13 16:56:38 +0000648SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400649 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000650}
651
Mike Reed353196f2017-07-21 11:01:18 -0400652bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000653 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400654 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000655}
656
Mike Reed353196f2017-07-21 11:01:18 -0400657bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
658 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400659}
660
661bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
662 SkPixmap pm;
663 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
664}
665
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000666bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400667 SkPixmap pm;
668 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700669 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000670 }
671 return false;
672}
673
Matt Sarett03dd6d52017-01-23 12:15:09 -0500674bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000675 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000676 SkBaseDevice* device = this->getDevice();
677 if (!device) {
678 return false;
679 }
680
Matt Sarett03dd6d52017-01-23 12:15:09 -0500681 // This check gives us an early out and prevents generation ID churn on the surface.
682 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
683 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400684 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500685 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000686 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000687
Matt Sarett03dd6d52017-01-23 12:15:09 -0500688 // Tell our owning surface to bump its generation ID.
689 const bool completeOverwrite =
690 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700691 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700692
Matt Sarett03dd6d52017-01-23 12:15:09 -0500693 // This can still fail, most notably in the case of a invalid color type or alpha type
694 // conversion. We could pull those checks into this function and avoid the unnecessary
695 // generation ID bump. But then we would be performing those checks twice, since they
696 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400697 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000698}
reed@google.com51df9e32010-12-23 19:29:18 +0000699
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700//////////////////////////////////////////////////////////////////////////////
701
reed2ff1fce2014-12-11 07:07:37 -0800702void SkCanvas::checkForDeferredSave() {
703 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800704 this->doSave();
705 }
706}
707
reedf0090cb2014-11-26 08:55:51 -0800708int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800709#ifdef SK_DEBUG
710 int count = 0;
711 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
712 for (;;) {
713 const MCRec* rec = (const MCRec*)iter.next();
714 if (!rec) {
715 break;
716 }
717 count += 1 + rec->fDeferredSaveCount;
718 }
719 SkASSERT(count == fSaveCount);
720#endif
721 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800722}
723
724int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800725 fSaveCount += 1;
726 fMCRec->fDeferredSaveCount += 1;
727 return this->getSaveCount() - 1; // return our prev value
728}
729
730void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800731 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700732
733 SkASSERT(fMCRec->fDeferredSaveCount > 0);
734 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800735 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800736}
737
Mike Reedc43f2a02020-01-16 14:54:34 -0500738int SkCanvas::experimental_saveCamera(const SkMatrix44& projection, const SkMatrix44& camera) {
Mike Reedee0a03a2020-01-14 16:44:47 -0500739 // TODO: add a virtual for this, and update clients (e.g. chrome)
740 int n = this->save();
Mike Reedd4d3b332020-01-16 16:34:34 -0500741 this->experimental_concat44(projection * camera);
Mike Reedb18e74d2020-01-16 13:58:22 -0500742 fCameraStack.push_back(CameraRec(fMCRec, camera));
Mike Reedee0a03a2020-01-14 16:44:47 -0500743 return n;
744}
745
reedf0090cb2014-11-26 08:55:51 -0800746void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800747 if (fMCRec->fDeferredSaveCount > 0) {
748 SkASSERT(fSaveCount > 1);
749 fSaveCount -= 1;
750 fMCRec->fDeferredSaveCount -= 1;
751 } else {
752 // check for underflow
753 if (fMCStack.count() > 1) {
754 this->willRestore();
755 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700756 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800757 this->internalRestore();
758 this->didRestore();
759 }
reedf0090cb2014-11-26 08:55:51 -0800760 }
761}
762
763void SkCanvas::restoreToCount(int count) {
764 // sanity check
765 if (count < 1) {
766 count = 1;
767 }
mtkleinf0f14112014-12-12 08:46:25 -0800768
reedf0090cb2014-11-26 08:55:51 -0800769 int n = this->getSaveCount() - count;
770 for (int i = 0; i < n; ++i) {
771 this->restore();
772 }
773}
774
reed2ff1fce2014-12-11 07:07:37 -0800775void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700777 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000779
Mike Reedc42a1cd2017-02-14 14:25:14 -0500780 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781}
782
reed4960eee2015-12-18 07:09:18 -0800783bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400784 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785}
786
reed4960eee2015-12-18 07:09:18 -0800787bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700788 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400789 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
790 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
791 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
792 // filter does not require a decomposed CTM matrix, the filter space and device space are the
793 // same. When it has been decomposed, we want the original image filter node to process the
794 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
795 // longer the original filter, but has the remainder matrix baked into it, and passing in the
796 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
797 // to the original filter node (barring inflation from consecutive calls to mapRect). While
798 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
799 // passing getDeviceClipBounds() to 'imageFilter' is correct.
800 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
801 // be important to more accurately calculate the clip bounds in the layer space for the original
802 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500803 SkIRect clipBounds = this->getDeviceClipBounds();
804 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000805 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000806 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000807
reed96e657d2015-03-10 17:30:07 -0700808 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
809
Robert Phillips12078432018-05-17 11:17:39 -0400810 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
811 // If the image filter DAG affects transparent black then we will need to render
812 // out to the clip bounds
813 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000814 }
Robert Phillips12078432018-05-17 11:17:39 -0400815
816 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700817 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700819 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400820 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400822 inputSaveLayerBounds = clipBounds;
823 }
824
825 if (imageFilter) {
826 // expand the clip bounds by the image filter DAG to include extra content that might
827 // be required by the image filters.
828 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
829 SkImageFilter::kReverse_MapDirection,
830 &inputSaveLayerBounds);
831 }
832
833 SkIRect clippedSaveLayerBounds;
834 if (bounds) {
835 // For better or for worse, user bounds currently act as a hard clip on the layer's
836 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
837 clippedSaveLayerBounds = inputSaveLayerBounds;
838 } else {
839 // If there are no user bounds, we don't want to artificially restrict the resulting
840 // layer bounds, so allow the expanded clip bounds free reign.
841 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800843
844 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400845 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800846 if (BoundsAffectsClip(saveLayerFlags)) {
847 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
848 fMCRec->fRasterClip.setEmpty();
849 fDeviceClipBounds.setEmpty();
850 }
851 return false;
852 }
Robert Phillips12078432018-05-17 11:17:39 -0400853 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854
reed4960eee2015-12-18 07:09:18 -0800855 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700856 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400857 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
858 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000859 }
860
861 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400862 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000863 }
Robert Phillips12078432018-05-17 11:17:39 -0400864
junov@chromium.orga907ac32012-02-24 21:54:07 +0000865 return true;
866}
867
reed4960eee2015-12-18 07:09:18 -0800868int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
869 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000870}
871
Cary Clarke041e312018-03-06 13:00:52 -0500872int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700873 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400874 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
875 // no need for the layer (or any of the draws until the matching restore()
876 this->save();
877 this->clipRect({0,0,0,0});
878 } else {
879 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
880 fSaveCount += 1;
881 this->internalSaveLayer(rec, strategy);
882 }
reed4960eee2015-12-18 07:09:18 -0800883 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800884}
885
Mike Reed148b7fd2018-12-18 17:38:18 -0500886int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
887 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
888 // Assuming clips never expand, if the request bounds is outside of the current clip
889 // there is no need to copy/restore the area, so just devolve back to a regular save.
890 this->save();
891 } else {
892 bool doTheWork = this->onDoSaveBehind(bounds);
893 fSaveCount += 1;
894 this->internalSave();
895 if (doTheWork) {
896 this->internalSaveBehind(bounds);
897 }
898 }
899 return this->getSaveCount() - 1;
900}
901
reeda2217ef2016-07-20 06:04:34 -0700902void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500903 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500904 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400905 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
906 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400907 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig915b7792019-10-22 17:40:41 +0000908
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400909 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400910 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
911 // This means that we only have to copy a dst-sized block of pixels out of src and translate
912 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400913 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
914 dstOrigin.y() - src->getOrigin().y(),
915 dst->width(), dst->height());
916 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400917 return;
918 }
919
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400920 auto special = src->snapSpecial(snapBounds);
921 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400922 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
923 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400924 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
925 }
926 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400927 }
928
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400929 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
930 // by the backdrop filter.
931 SkMatrix toRoot, layerMatrix;
932 SkSize scale;
933 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
934 toRoot = SkMatrix::I();
935 layerMatrix = ctm;
936 } else if (ctm.decomposeScale(&scale, &toRoot)) {
937 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
938 } else {
939 // Perspective, for now, do no scaling of the layer itself.
940 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
941 // the matrix, e.g. based on the midpoint of the near/far planes?
942 toRoot = ctm;
943 layerMatrix = SkMatrix::I();
944 }
945
946 // We have to map the dst bounds from the root space into the layer space where filtering will
947 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
948 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
949 // is a safe, conservative estimate.
950 SkMatrix fromRoot;
951 if (!toRoot.invert(&fromRoot)) {
952 return;
953 }
954
955 // This represents what the backdrop filter needs to produce in the layer space, and is sized
956 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
957 SkIRect layerTargetBounds = fromRoot.mapRect(
958 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
959 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
960 // require some extra input pixels.
961 SkIRect layerInputBounds = filter->filterBounds(
962 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
963 &layerTargetBounds);
964
965 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400966 // be the conservative contents required to fill a layerInputBounds-sized surface with the
967 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400968 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
969 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
970 if (!backdropBounds.intersect(srcDevRect)) {
971 return;
972 }
973
974 auto special = src->snapSpecial(backdropBounds);
975 if (!special) {
976 return;
977 }
978
979 SkColorType colorType = src->imageInfo().colorType();
980 if (colorType == kUnknown_SkColorType) {
981 colorType = kRGBA_8888_SkColorType;
982 }
983 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400984
985 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400986 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400987 // Drawing the temporary and final filtered image requires a higher filter quality if the
988 // 'toRoot' transformation is not identity, in order to minimize the impact on already
989 // rendered edges/content.
990 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
991 p.setFilterQuality(kHigh_SkFilterQuality);
992
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400993 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
994 // and stored in a temporary surface, which is then used as the input to the actual filter.
995 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
996 if (!tmpSurface) {
997 return;
998 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400999
1000 auto tmpCanvas = tmpSurface->getCanvas();
1001 tmpCanvas->clear(SK_ColorTRANSPARENT);
1002 // Reading in reverse, this takes the backdrop bounds from src device space into the root
1003 // space, then maps from root space into the layer space, then maps it so the input layer's
1004 // top left corner is (0, 0). This transformation automatically accounts for any cropping
1005 // performed on backdropBounds.
1006 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1007 tmpCanvas->concat(fromRoot);
1008 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001009
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001010 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1011 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1012 special = tmpSurface->makeImageSnapshot();
1013 } else {
1014 // Since there is no extra transform that was done, update the input bounds to reflect
1015 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1016 // was equal to backdropBounds before it was made relative to the src device and cropped.
1017 // When we use the original snapped image directly, just map the update backdrop bounds
1018 // back into the shared layer space
1019 layerInputBounds = backdropBounds;
1020 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001021
1022 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1023 // draw will be 1-1 so there is no need to increase filter quality.
1024 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001025 }
1026
1027 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1028 // layer space. This has to further offset everything so that filter evaluation thinks the
1029 // source image's top left corner is (0, 0).
1030 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1031 // this can be simplified.
1032 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1033 SkMatrix filterCTM = layerMatrix;
1034 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1035 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1036
1037 SkIPoint offset;
1038 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001039 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001040 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1041 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1042 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1043 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001044 offset += layerInputBounds.topLeft();
1045
1046 // Manually setting the device's CTM requires accounting for the device's origin.
1047 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001048 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001049 // a global CTM instead of a device CTM.
1050 SkMatrix dstCTM = toRoot;
1051 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001052 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001053
1054 // And because devices don't have a special-image draw function that supports arbitrary
1055 // matrices, we are abusing the asImage() functionality here...
1056 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001057 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001058 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001059 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001060 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1061 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001062 }
robertphillips7354a4b2015-12-16 05:08:27 -08001063}
reed70ee31b2015-12-10 13:44:45 -08001064
Mike Kleine083f7c2018-02-07 12:54:27 -05001065static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001066 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001067 if (prev.bytesPerPixel() <= 4 &&
1068 prev.colorType() != kRGBA_8888_SkColorType &&
1069 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001070 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1071 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1072 ct = kN32_SkColorType;
1073 }
1074 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001075}
1076
reed4960eee2015-12-18 07:09:18 -08001077void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001078 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001079 const SkRect* bounds = rec.fBounds;
1080 const SkPaint* paint = rec.fPaint;
1081 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1082
Mike Reed5532c2a2019-02-23 12:00:32 -05001083 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1084 // regardless of any hint-rect from the caller. skbug.com/8783
1085 if (rec.fBackdrop) {
1086 bounds = nullptr;
1087 }
1088
reed8c30a812016-04-20 16:36:51 -07001089 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001090 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001091 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001092 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001093
reed8c30a812016-04-20 16:36:51 -07001094 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001095 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1096 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1097 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001098 *
1099 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001100 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1101 * if necessary.
1102 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1103 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001104 * 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 -04001105 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001106 * of the original imagefilter, and draw that (via drawSprite)
1107 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1108 *
1109 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1110 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1111 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001112 if (imageFilter) {
1113 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001114 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1115 &modifiedCTM);
1116 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001117 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001118 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001119 modifiedRec = fMCRec;
1120 this->internalSetMatrix(modifiedCTM);
1121 SkPaint* p = lazyP.set(*paint);
1122 p->setImageFilter(std::move(modifiedFilter));
1123 imageFilter = p->getImageFilter();
1124 paint = p;
1125 }
1126 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1127 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001128 }
reed8c30a812016-04-20 16:36:51 -07001129
junov@chromium.orga907ac32012-02-24 21:54:07 +00001130 // do this before we create the layer. We don't call the public save() since
1131 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001132 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001133
junov@chromium.orga907ac32012-02-24 21:54:07 +00001134 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001135 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001136 if (modifiedRec) {
1137 // In this case there will be no layer in which to stash the matrix so we need to
1138 // revert the prior MCRec to its earlier state.
1139 modifiedRec->fMatrix = stashedMatrix;
1140 }
reed2ff1fce2014-12-11 07:07:37 -08001141 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 }
1143
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001144 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1145 // the clipRectBounds() call above?
1146 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001147 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001148 }
1149
reed8dc0ccb2015-03-20 06:32:52 -07001150 SkPixelGeometry geo = fProps.pixelGeometry();
1151 if (paint) {
reed76033be2015-03-14 10:54:31 -07001152 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001153 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001154 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001155 }
1156 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157
robertphillips5139e502016-07-19 05:10:40 -07001158 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001159 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001160 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001161 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001162 }
reedb2db8982014-11-13 12:41:02 -08001163
Mike Kleine083f7c2018-02-07 12:54:27 -05001164 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001165 if (rec.fSaveLayerFlags & kF16ColorType) {
1166 info = info.makeColorType(kRGBA_F16_SkColorType);
1167 }
reed129ed1c2016-02-22 06:42:31 -08001168
Hal Canary704cd322016-11-07 14:13:52 -05001169 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001170 {
Florin Malita4571e492019-07-16 10:25:58 -04001171 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001172 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001173 const bool trackCoverage =
1174 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001175 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001176 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001177 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001178 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1179 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001180 return;
reed61f501f2015-04-29 08:34:00 -07001181 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001182 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001183 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184
Mike Reedb43a3e02017-02-11 10:18:58 -05001185 // only have a "next" if this new layer doesn't affect the clip (rare)
1186 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 fMCRec->fLayer = layer;
1188 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001189
Mike Reedc61abee2017-02-28 17:45:27 -05001190 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001191 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001192 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001193 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001194
Mike Reedc42a1cd2017-02-14 14:25:14 -05001195 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1196
1197 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1198 if (layer->fNext) {
1199 // need to punch a hole in the previous device, so we don't draw there, given that
1200 // the new top-layer will allow drawing to happen "below" it.
1201 SkRegion hole(ir);
1202 do {
1203 layer = layer->fNext;
1204 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1205 } while (layer->fNext);
1206 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207}
1208
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001209int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001210 if (0xFF == alpha) {
1211 return this->saveLayer(bounds, nullptr);
1212 } else {
1213 SkPaint tmpPaint;
1214 tmpPaint.setAlpha(alpha);
1215 return this->saveLayer(bounds, &tmpPaint);
1216 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001217}
1218
Mike Reed148b7fd2018-12-18 17:38:18 -05001219void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001220 SkIRect devBounds;
1221 if (localBounds) {
1222 SkRect tmp;
1223 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1224 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1225 devBounds.setEmpty();
1226 }
1227 } else {
1228 devBounds = this->getDeviceClipBounds();
1229 }
1230 if (devBounds.isEmpty()) {
1231 return;
1232 }
1233
Mike Reed148b7fd2018-12-18 17:38:18 -05001234 SkBaseDevice* device = this->getTopDevice();
1235 if (nullptr == device) { // Do we still need this check???
1236 return;
1237 }
1238
Michael Ludwig915b7792019-10-22 17:40:41 +00001239 // need the bounds relative to the device itself
1240 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
Mike Reed148b7fd2018-12-18 17:38:18 -05001241
Michael Ludwigac352122019-08-28 21:03:05 +00001242 // This is getting the special image from the current device, which is then drawn into (both by
1243 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1244 // own device, we need to explicitly copy the back image contents so that its original content
1245 // is available when we splat it back later during restore.
1246 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001247 if (!backImage) {
1248 return;
1249 }
1250
1251 // we really need the save, so we can wack the fMCRec
1252 this->checkForDeferredSave();
1253
1254 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1255
1256 SkPaint paint;
1257 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001258 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001259}
1260
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261void SkCanvas::internalRestore() {
1262 SkASSERT(fMCStack.count() != 0);
1263
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001264 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 DeviceCM* layer = fMCRec->fLayer; // may be null
1266 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001267 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268
Mike Reed148b7fd2018-12-18 17:38:18 -05001269 // move this out before we do the actual restore
1270 auto backImage = std::move(fMCRec->fBackImage);
1271
Mike Reedb18e74d2020-01-16 13:58:22 -05001272 if (!fCameraStack.empty() && fCameraStack.back().fMCRec == fMCRec) {
1273 fCameraStack.pop_back();
1274 }
1275
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 // now do the normal restore()
1277 fMCRec->~MCRec(); // balanced in save()
1278 fMCStack.pop_back();
1279 fMCRec = (MCRec*)fMCStack.back();
1280
Mike Reedc42a1cd2017-02-14 14:25:14 -05001281 if (fMCRec) {
1282 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1283 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001284
Mike Reed148b7fd2018-12-18 17:38:18 -05001285 if (backImage) {
1286 SkPaint paint;
1287 paint.setBlendMode(SkBlendMode::kDstOver);
1288 const int x = backImage->fLoc.x();
1289 const int y = backImage->fLoc.y();
1290 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1291 nullptr, SkMatrix::I());
1292 }
1293
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1295 since if we're being recorded, we don't want to record this (the
1296 recorder will have already recorded the restore).
1297 */
bsalomon49f085d2014-09-05 13:34:00 -07001298 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001299 if (fMCRec) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001300 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001301 layer->fDevice->setImmutable();
Michael Ludwig915b7792019-10-22 17:40:41 +00001302 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
1303 layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001304 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001305 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001306 this->internalSetMatrix(layer->fStashedMatrix);
Michael Ludwig915b7792019-10-22 17:40:41 +00001307 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001308 delete layer;
reedb679ca82015-04-07 04:40:48 -07001309 } else {
1310 // we're at the root
reeda499f902015-05-01 09:34:31 -07001311 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001312 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001313 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001315 }
msarettfbfa2582016-08-12 08:29:08 -07001316
1317 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001318 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001319 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321}
1322
reede8f30622016-03-23 18:59:25 -07001323sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001324 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001325 props = &fProps;
1326 }
1327 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001328}
1329
reede8f30622016-03-23 18:59:25 -07001330sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001331 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001332 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001333}
1334
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001335SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001336 return this->onImageInfo();
1337}
1338
1339SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001340 SkBaseDevice* dev = this->getDevice();
1341 if (dev) {
1342 return dev->imageInfo();
1343 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001344 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001345 }
1346}
1347
brianosman898235c2016-04-06 07:38:23 -07001348bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001349 return this->onGetProps(props);
1350}
1351
1352bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001353 SkBaseDevice* dev = this->getDevice();
1354 if (dev) {
1355 if (props) {
1356 *props = fProps;
1357 }
1358 return true;
1359 } else {
1360 return false;
1361 }
1362}
1363
reed6ceeebd2016-03-09 14:26:26 -08001364bool SkCanvas::peekPixels(SkPixmap* pmap) {
1365 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001366}
1367
reed884e97c2015-05-26 11:31:54 -07001368bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001369 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001370 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001371}
1372
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001373void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001374 SkPixmap pmap;
1375 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001376 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001377 }
1378 if (info) {
1379 *info = pmap.info();
1380 }
1381 if (rowBytes) {
1382 *rowBytes = pmap.rowBytes();
1383 }
1384 if (origin) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001385 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001386 }
reed884e97c2015-05-26 11:31:54 -07001387 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001388}
1389
reed884e97c2015-05-26 11:31:54 -07001390bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001391 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001392 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001393}
1394
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396
Mike Reed8bcd1282019-03-13 16:51:54 -04001397// In our current design/features, we should never have a layer (src) in a different colorspace
1398// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1399// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1400// colorspace.
1401static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1402 SkASSERT(src == dst);
1403}
1404
Michael Ludwig915b7792019-10-22 17:40:41 +00001405void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001406 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001408 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 paint = &tmp;
1410 }
reed@google.com4b226022011-01-11 18:32:13 +00001411
Mike Reed38992392019-07-30 10:48:15 -04001412 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001415 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001416 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1417 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001418 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001419 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwig915b7792019-10-22 17:40:41 +00001420 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001421 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001422 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1423 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001424 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1425 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001426 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1427 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001428 }
reed@google.com76dd2772012-01-05 21:15:07 +00001429 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001430 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001431 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 }
reeda2217ef2016-07-20 06:04:34 -07001433
Mike Reed38992392019-07-30 10:48:15 -04001434 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435}
1436
reed32704672015-12-16 08:27:10 -08001437/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001438
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001439void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001440 if (dx || dy) {
1441 this->checkForDeferredSave();
Mike Reedb18e74d2020-01-16 13:58:22 -05001442 fMCRec->fMatrix.preTranslate(dx, dy);
mtkleincbdf0072016-08-19 09:05:27 -07001443
reedfe69b502016-09-12 06:31:48 -07001444 // Translate shouldn't affect the is-scale-translateness of the matrix.
Mike Reed403c8072020-01-08 10:40:39 -05001445 // However, if either is non-finite, we might still complicate the matrix type,
1446 // so we still have to compute this.
1447 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
mtkleincbdf0072016-08-19 09:05:27 -07001448
Mike Reedc42a1cd2017-02-14 14:25:14 -05001449 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001450
reedfe69b502016-09-12 06:31:48 -07001451 this->didTranslate(dx,dy);
1452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453}
1454
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001455void SkCanvas::scale(SkScalar sx, SkScalar sy) {
Mike Reed9403c382020-01-13 14:40:56 +00001456 if (sx != 1 || sy != 1) {
1457 this->checkForDeferredSave();
1458 fMCRec->fMatrix.preScale(sx, sy);
1459
1460 // shouldn't need to do this (theoretically), as the state shouldn't have changed,
1461 // but pre-scaling by a non-finite does change it, so we have to recompute.
1462 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1463
1464 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1465
1466 this->didScale(sx, sy);
1467 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468}
1469
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001470void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001471 SkMatrix m;
1472 m.setRotate(degrees);
1473 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
bungeman7438bfc2016-07-12 15:01:19 -07001476void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1477 SkMatrix m;
1478 m.setRotate(degrees, px, py);
1479 this->concat(m);
1480}
1481
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001482void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001483 SkMatrix m;
1484 m.setSkew(sx, sy);
1485 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001486}
1487
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001488void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001489 if (matrix.isIdentity()) {
1490 return;
1491 }
1492
reed2ff1fce2014-12-11 07:07:37 -08001493 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001494 fMCRec->fMatrix.preConcat(matrix);
Mike Reedb18e74d2020-01-16 13:58:22 -05001495
msarett9637ea92016-08-18 14:03:30 -07001496 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001497
Mike Reed7627fa52017-02-08 10:07:53 -05001498 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001499
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001500 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001501}
1502
Mike Reedc43f2a02020-01-16 14:54:34 -05001503void SkCanvas::experimental_concat44(const SkScalar m[16]) {
Mike Reed403c8072020-01-08 10:40:39 -05001504 this->checkForDeferredSave();
1505
Mike Reedb18e74d2020-01-16 13:58:22 -05001506 fMCRec->fMatrix.preConcat16(m);
Mike Reed403c8072020-01-08 10:40:39 -05001507
1508 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1509
1510 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
1511
1512 this->didConcat44(m);
1513}
Mike Reed064c7f92020-01-08 17:33:04 -05001514
Mike Reedd4d3b332020-01-16 16:34:34 -05001515void SkCanvas::experimental_concat44(const SkMatrix44& m) {
Mike Reedc43f2a02020-01-16 14:54:34 -05001516 this->experimental_concat44(m.values());
Mike Reed064c7f92020-01-08 17:33:04 -05001517}
Mike Reed403c8072020-01-08 10:40:39 -05001518
Mike Reedee3216d2020-01-17 17:35:04 -05001519void SkCanvas::experimental_concat44(const SkM44& m) {
Mike Reed07d32b42020-01-23 11:06:20 -05001520 this->experimental_concat44(SkMatrixPriv::M44ColMajor(m));
Mike Reedee3216d2020-01-17 17:35:04 -05001521}
1522
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 Reedc43f2a02020-01-16 14:54:34 -05001825SkM44 SkCanvas::experimental_getLocalToDevice() const {
Mike Reed75435872020-01-13 21:15:06 -05001826 return fMCRec->fMatrix;
1827}
1828
Mike Reedc43f2a02020-01-16 14:54:34 -05001829SkM44 SkCanvas::experimental_getLocalToWorld() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001830 if (fCameraStack.empty()) {
Mike Reedc43f2a02020-01-16 14:54:34 -05001831 return this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001832 } else {
1833 const auto& top = fCameraStack.back();
Mike Reedc43f2a02020-01-16 14:54:34 -05001834 return top.fInvPostCamera * this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001835 }
1836}
1837
Mike Reedc43f2a02020-01-16 14:54:34 -05001838SkM44 SkCanvas::experimental_getLocalToCamera() const {
Mike Reedb18e74d2020-01-16 13:58:22 -05001839 if (fCameraStack.empty()) {
Mike Reedc43f2a02020-01-16 14:54:34 -05001840 return this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001841 } else {
1842 const auto& top = fCameraStack.back();
Mike Reedc43f2a02020-01-16 14:54:34 -05001843 return top.fCamera * top.fInvPostCamera * this->experimental_getLocalToDevice();
Mike Reedb18e74d2020-01-16 13:58:22 -05001844 }
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///////////////////////////////////////////////////////////////////////////////////////////////////