blob: 9dd0f16b0b69f3a2f1c592bb0a3755c51fa3a099 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkImage.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkRRect.h"
16#include "include/core/SkRasterHandleAllocator.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTextBlob.h"
19#include "include/core/SkVertices.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/private/SkNx.h"
21#include "include/private/SkTo.h"
22#include "include/utils/SkNoDrawCanvas.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040023#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkBitmapDevice.h"
25#include "src/core/SkCanvasPriv.h"
26#include "src/core/SkClipOpPriv.h"
27#include "src/core/SkClipStack.h"
28#include "src/core/SkDraw.h"
29#include "src/core/SkGlyphRun.h"
30#include "src/core/SkImageFilterCache.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040031#include "src/core/SkImageFilter_Base.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "src/core/SkLatticeIter.h"
33#include "src/core/SkMSAN.h"
34#include "src/core/SkMakeUnique.h"
35#include "src/core/SkMatrixUtils.h"
36#include "src/core/SkPaintPriv.h"
37#include "src/core/SkRasterClip.h"
38#include "src/core/SkSpecialImage.h"
39#include "src/core/SkStrikeCache.h"
40#include "src/core/SkTLazy.h"
41#include "src/core/SkTextFormatParams.h"
42#include "src/core/SkTraceEvent.h"
43#include "src/image/SkImage_Base.h"
44#include "src/image/SkSurface_Base.h"
45#include "src/utils/SkPatchUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040046
bungemand3ebb482015-08-05 13:57:49 -070047#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000048
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000049#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050050#include "include/gpu/GrContext.h"
51#include "src/gpu/SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052#endif
53
reede3b38ce2016-01-08 09:18:44 -080054#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
Mike Reed74d6e112018-01-23 13:06:12 -050055#define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
reede3b38ce2016-01-08 09:18:44 -080056
Brian Salomonb0047b52019-12-05 19:52:25 +000057// This is a test: static_assert with no message is a c++17 feature.
58static_assert(true);
59
Mike Reed139e5e02017-03-08 11:29:33 -050060///////////////////////////////////////////////////////////////////////////////////////////////////
61
reedc83a2972015-07-16 07:40:45 -070062/*
63 * Return true if the drawing this rect would hit every pixels in the canvas.
64 *
65 * Returns false if
66 * - rect does not contain the canvas' bounds
67 * - paint is not fill
68 * - paint would blur or otherwise change the coverage of the rect
69 */
70bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
71 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070072 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
73 (int)kNone_ShaderOverrideOpacity,
74 "need_matching_enums0");
75 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
76 (int)kOpaque_ShaderOverrideOpacity,
77 "need_matching_enums1");
78 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
79 (int)kNotOpaque_ShaderOverrideOpacity,
80 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070081
82 const SkISize size = this->getBaseLayerSize();
83 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050084
85 // if we're clipped at all, we can't overwrite the entire surface
86 {
87 SkBaseDevice* base = this->getDevice();
88 SkBaseDevice* top = this->getTopDevice();
89 if (base != top) {
90 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
91 }
92 if (!base->clipIsWideOpen()) {
93 return false;
94 }
reedc83a2972015-07-16 07:40:45 -070095 }
96
97 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070098 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070099 return false; // conservative
100 }
halcanaryc5769b22016-08-10 07:13:21 -0700101
102 SkRect devRect;
103 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
104 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700105 return false;
106 }
107 }
108
109 if (paint) {
110 SkPaint::Style paintStyle = paint->getStyle();
111 if (!(paintStyle == SkPaint::kFill_Style ||
112 paintStyle == SkPaint::kStrokeAndFill_Style)) {
113 return false;
114 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400115 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700116 return false; // conservative
117 }
118 }
119 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
120}
121
122///////////////////////////////////////////////////////////////////////////////////////////////////
123
reed@google.comda17f752012-08-16 18:27:05 +0000124// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125//#define SK_TRACE_SAVERESTORE
126
127#ifdef SK_TRACE_SAVERESTORE
128 static int gLayerCounter;
129 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
130 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
131
132 static int gRecCounter;
133 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
134 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
135
136 static int gCanvasCounter;
137 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
138 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
139#else
140 #define inc_layer()
141 #define dec_layer()
142 #define inc_rec()
143 #define dec_rec()
144 #define inc_canvas()
145 #define dec_canvas()
146#endif
147
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000148typedef SkTLazy<SkPaint> SkLazyPaint;
149
reedc83a2972015-07-16 07:40:45 -0700150void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000151 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700152 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
153 ? SkSurface::kDiscard_ContentChangeMode
154 : SkSurface::kRetain_ContentChangeMode);
155 }
156}
157
158void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
159 ShaderOverrideOpacity overrideOpacity) {
160 if (fSurfaceBase) {
161 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
162 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
163 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
164 // and therefore we don't care which mode we're in.
165 //
166 if (fSurfaceBase->outstandingImageSnapshot()) {
167 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
168 mode = SkSurface::kDiscard_ContentChangeMode;
169 }
170 }
171 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000172 }
173}
174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000177/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 The clip/matrix/proc are fields that reflect the top of the save/restore
179 stack. Whenever the canvas changes, it marks a dirty flag, and then before
180 these are used (assuming we're not on a layer) we rebuild these cache
181 values: they reflect the top of the save stack, but translated and clipped
182 by the device's XY offset and bitmap-bounds.
183*/
184struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400185 DeviceCM* fNext;
186 sk_sp<SkBaseDevice> fDevice;
187 SkRasterClip fClip;
188 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
189 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400190 sk_sp<SkImage> fClipImage;
191 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192
Florin Malita53f77bd2017-04-28 13:48:37 -0400193 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000194 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700195 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400196 , fDevice(std::move(device))
197 , fPaint(paint ? skstd::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700198 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000199 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400200 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400201 {}
reed@google.com4b226022011-01-11 18:32:13 +0000202
mtkleinfeaadee2015-04-08 11:25:48 -0700203 void reset(const SkIRect& bounds) {
204 SkASSERT(!fPaint);
205 SkASSERT(!fNext);
206 SkASSERT(fDevice);
207 fClip.setRect(bounds);
208 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209};
210
Mike Reed148b7fd2018-12-18 17:38:18 -0500211namespace {
212// Encapsulate state needed to restore from saveBehind()
213struct BackImage {
214 sk_sp<SkSpecialImage> fImage;
215 SkIPoint fLoc;
216};
217}
218
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219/* This is the record we keep for each save/restore level in the stack.
220 Since a level optionally copies the matrix and/or stack, we have pointers
221 for these fields. If the value is copied for this level, the copy is
222 stored in the ...Storage field, and the pointer points to that. If the
223 value is not copied for this level, we ignore ...Storage, and just point
224 at the corresponding value in the previous level in the stack.
225*/
226class SkCanvas::MCRec {
227public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500228 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 /* If there are any layers in the stack, this points to the top-most
230 one that is at or below this level in the stack (so we know what
231 bitmap/device to draw into from this level. This value is NOT
232 reference counted, since the real owner is either our fLayer field,
233 or a previous one in a lower level.)
234 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500235 DeviceCM* fTopLayer;
236 std::unique_ptr<BackImage> fBackImage;
237 SkConservativeClip fRasterClip;
238 SkMatrix fMatrix;
239 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240
Mike Reeda1361362017-03-07 09:37:29 -0500241 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700242 fLayer = nullptr;
243 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800244 fMatrix.reset();
245 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700246
reedd9544982014-09-09 18:46:22 -0700247 // don't bother initializing fNext
248 inc_rec();
249 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400250 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700251 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700252 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800253 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700254
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 // don't bother initializing fNext
256 inc_rec();
257 }
258 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700259 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 dec_rec();
261 }
mtkleinfeaadee2015-04-08 11:25:48 -0700262
263 void reset(const SkIRect& bounds) {
264 SkASSERT(fLayer);
265 SkASSERT(fDeferredSaveCount == 0);
266
267 fMatrix.reset();
268 fRasterClip.setRect(bounds);
269 fLayer->reset(bounds);
270 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271};
272
Mike Reeda1361362017-03-07 09:37:29 -0500273class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274public:
Mike Reeda1361362017-03-07 09:37:29 -0500275 SkDrawIter(SkCanvas* canvas)
276 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
277 {}
reed@google.com4b226022011-01-11 18:32:13 +0000278
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000280 const DeviceCM* rec = fCurrLayer;
281 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400282 fDevice = rec->fDevice.get();
283 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700285 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 return true;
287 }
288 return false;
289 }
reed@google.com4b226022011-01-11 18:32:13 +0000290
Michael Ludwig915b7792019-10-22 17:40:41 +0000291 int getX() const { return fDevice->getOrigin().x(); }
292 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000294
Mike Reed99330ba2017-02-22 11:01:08 -0500295 SkBaseDevice* fDevice;
296
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 const DeviceCM* fCurrLayer;
299 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300};
301
Florin Malita713b8ef2017-04-28 10:57:24 -0400302#define FOR_EACH_TOP_DEVICE( code ) \
303 do { \
304 DeviceCM* layer = fMCRec->fTopLayer; \
305 while (layer) { \
306 SkBaseDevice* device = layer->fDevice.get(); \
307 if (device) { \
308 code; \
309 } \
310 layer = layer->fNext; \
311 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500312 } while (0)
313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314/////////////////////////////////////////////////////////////////////////////
315
reeddbc3cef2015-04-29 12:18:57 -0700316/**
317 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700318 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700319 */
reedd053ce92016-03-22 10:17:23 -0700320static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700321 SkImageFilter* imgf = paint.getImageFilter();
322 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700323 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700324 }
325
reedd053ce92016-03-22 10:17:23 -0700326 SkColorFilter* imgCFPtr;
327 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700328 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700329 }
reedd053ce92016-03-22 10:17:23 -0700330 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700331
332 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700333 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700334 // there is no existing paint colorfilter, so we can just return the imagefilter's
335 return imgCF;
336 }
337
338 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
339 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500340 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700341}
342
senorblanco87e066e2015-10-28 11:23:36 -0700343/**
344 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
345 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
346 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
347 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
348 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
349 * conservative "effective" bounds based on the settings in the paint... with one exception. This
350 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
351 * deliberately ignored.
352 */
353static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
354 const SkRect& rawBounds,
355 SkRect* storage) {
356 SkPaint tmpUnfiltered(paint);
357 tmpUnfiltered.setImageFilter(nullptr);
358 if (tmpUnfiltered.canComputeFastBounds()) {
359 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
360 } else {
361 return rawBounds;
362 }
363}
364
Mike Reed38992392019-07-30 10:48:15 -0400365class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366public:
senorblanco87e066e2015-10-28 11:23:36 -0700367 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
368 // paint. It's used to determine the size of the offscreen layer for filters.
369 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400370 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
371 bool skipLayerForImageFilter = false,
372 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400374 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700376 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
Mike Reed38992392019-07-30 10:48:15 -0400378 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
379 SkASSERT(!fLazyPaint.isValid());
380 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700381 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700382 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700383 fPaint = paint;
384 }
385
386 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700387 /**
388 * We implement ImageFilters for a given draw by creating a layer, then applying the
389 * imagefilter to the pixels of that layer (its backing surface/image), and then
390 * we call restore() to xfer that layer to the main canvas.
391 *
392 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
393 * 2. Generate the src pixels:
394 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
395 * return (fPaint). We then draw the primitive (using srcover) into a cleared
396 * buffer/surface.
397 * 3. Restore the layer created in #1
398 * The imagefilter is passed the buffer/surface from the layer (now filled with the
399 * src pixels of the primitive). It returns a new "filtered" buffer, which we
400 * draw onto the previous layer using the xfermode from the original paint.
401 */
Mike Reed38992392019-07-30 10:48:15 -0400402
403 SkPaint restorePaint;
404 restorePaint.setImageFilter(fPaint->refImageFilter());
405 restorePaint.setBlendMode(fPaint->getBlendMode());
406
senorblanco87e066e2015-10-28 11:23:36 -0700407 SkRect storage;
408 if (rawBounds) {
409 // Make rawBounds include all paint outsets except for those due to image filters.
410 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
411 }
Mike Reed38992392019-07-30 10:48:15 -0400412 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700413 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700414 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000415
Mike Reed38992392019-07-30 10:48:15 -0400416 // Remove the restorePaint fields from our "working" paint
417 SkASSERT(!fLazyPaint.isValid());
418 SkPaint* paint = fLazyPaint.set(origPaint);
419 paint->setImageFilter(nullptr);
420 paint->setBlendMode(SkBlendMode::kSrcOver);
421 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000422 }
423 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000424
Mike Reed38992392019-07-30 10:48:15 -0400425 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700426 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000427 fCanvas->internalRestore();
428 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000429 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000431
reed@google.com4e2b3d32011-04-07 14:18:59 +0000432 const SkPaint& paint() const {
433 SkASSERT(fPaint);
434 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437private:
Mike Reed38992392019-07-30 10:48:15 -0400438 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000439 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400440 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000441 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700442 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443};
444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445////////// macros to place around the internal draw calls //////////////////
446
Mike Reed38992392019-07-30 10:48:15 -0400447#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700448 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400449 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
450 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800451
452
Mike Reed38992392019-07-30 10:48:15 -0400453#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000454 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400455 AutoLayerForImageFilter draw(this, paint, true); \
456 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000457
Mike Reed38992392019-07-30 10:48:15 -0400458#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000459 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400460 AutoLayerForImageFilter draw(this, paint, false, bounds); \
461 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000462
Mike Reed38992392019-07-30 10:48:15 -0400463#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700464 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400465 AutoLayerForImageFilter draw(this, paint, false, bounds); \
466 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700467
Mike Reed38992392019-07-30 10:48:15 -0400468#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469
470////////////////////////////////////////////////////////////////////////////
471
msarettfbfa2582016-08-12 08:29:08 -0700472static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
473 if (bounds.isEmpty()) {
474 return SkRect::MakeEmpty();
475 }
476
477 // Expand bounds out by 1 in case we are anti-aliasing. We store the
478 // bounds as floats to enable a faster quick reject implementation.
479 SkRect dst;
480 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
481 return dst;
482}
483
mtkleinfeaadee2015-04-08 11:25:48 -0700484void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
485 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700486 fMCRec->reset(bounds);
487
488 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500489 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400490 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700491 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700492 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700493}
494
Hal Canary363a3f82018-10-04 11:04:48 -0400495void SkCanvas::init(sk_sp<SkBaseDevice> device) {
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000496 fAllowSimplifyClip = false;
reed2ff1fce2014-12-11 07:07:37 -0800497 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500500 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500501 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700502 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503
reeda499f902015-05-01 09:34:31 -0700504 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
505 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400506 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509
halcanary96fcdcc2015-08-27 07:41:13 -0700510 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000511
reedf92c8662014-08-18 08:02:43 -0700512 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700513 // The root device and the canvas should always have the same pixel geometry
514 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800515 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700516 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500517
Mike Reedc42a1cd2017-02-14 14:25:14 -0500518 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700519 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400520
Herb Derby59d997a2018-06-07 12:44:09 -0400521 fScratchGlyphRunBuilder = skstd::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
reed@google.comcde92112011-07-06 20:00:52 +0000524SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700526 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000527{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000528 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000529
Hal Canary363a3f82018-10-04 11:04:48 -0400530 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000531}
532
reed96a857e2015-01-25 10:33:58 -0800533SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000534 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800535 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000536{
537 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400538 this->init(sk_make_sp<SkNoPixelsDevice>(
Hal Canary363a3f82018-10-04 11:04:48 -0400539 SkIRect::MakeWH(SkTMax(width, 0), SkTMax(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700540}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000541
Hal Canary363a3f82018-10-04 11:04:48 -0400542SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700543 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700544 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700545{
546 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700547
Mike Reed566e53c2017-03-10 10:49:45 -0500548 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400549 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700550}
551
Herb Derbyefe39bc2018-05-01 17:06:20 -0400552SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000553 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700554 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000555{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700557
Hal Canary363a3f82018-10-04 11:04:48 -0400558 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700559}
560
reed4a8126e2014-09-22 07:29:03 -0700561SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700562 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700563 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700564{
565 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700566
Mike Reed910ca0f2018-04-25 13:04:05 -0400567 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400568 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700569}
reed29c857d2014-09-21 10:25:07 -0700570
Mike Reed356f7c22017-01-10 11:58:39 -0500571SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
572 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700573 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
574 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500575 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700576{
577 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700578
Mike Reed910ca0f2018-04-25 13:04:05 -0400579 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400580 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581}
582
Mike Reed356f7c22017-01-10 11:58:39 -0500583SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
584
Matt Sarett31f99ce2017-04-11 08:46:01 -0400585#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
586SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
587 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
588 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
589 , fAllocator(nullptr)
590{
591 inc_canvas();
592
593 SkBitmap tmp(bitmap);
594 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400595 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400596 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400597}
598#endif
599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600SkCanvas::~SkCanvas() {
601 // free up the contents of our deque
602 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 this->internalRestore(); // restore the last, since we're going away
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 dec_canvas();
607}
608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609///////////////////////////////////////////////////////////////////////////////
610
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000611void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700612 this->onFlush();
613}
614
615void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000616 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000617 if (device) {
618 device->flush();
619 }
620}
621
Mike Reeda2b3b9e2019-11-15 15:00:27 -0500622SkSurface* SkCanvas::getSurface() const {
623 return fSurfaceBase;
624}
625
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000626SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000627 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000628 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
629}
630
senorblancoafc7cce2016-02-02 18:44:15 -0800631SkIRect SkCanvas::getTopLayerBounds() const {
632 SkBaseDevice* d = this->getTopDevice();
633 if (!d) {
634 return SkIRect::MakeEmpty();
635 }
Michael Ludwig915b7792019-10-22 17:40:41 +0000636 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
senorblancoafc7cce2016-02-02 18:44:15 -0800637}
638
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000639SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000641 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400643 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644}
645
Florin Malita0ed3b642017-01-13 16:56:38 +0000646SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400647 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000648}
649
Mike Reed353196f2017-07-21 11:01:18 -0400650bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000651 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400652 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000653}
654
Mike Reed353196f2017-07-21 11:01:18 -0400655bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
656 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400657}
658
659bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
660 SkPixmap pm;
661 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
662}
663
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000664bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400665 SkPixmap pm;
666 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700667 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000668 }
669 return false;
670}
671
Matt Sarett03dd6d52017-01-23 12:15:09 -0500672bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000673 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000674 SkBaseDevice* device = this->getDevice();
675 if (!device) {
676 return false;
677 }
678
Matt Sarett03dd6d52017-01-23 12:15:09 -0500679 // This check gives us an early out and prevents generation ID churn on the surface.
680 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
681 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400682 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500683 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000684 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000685
Matt Sarett03dd6d52017-01-23 12:15:09 -0500686 // Tell our owning surface to bump its generation ID.
687 const bool completeOverwrite =
688 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700689 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700690
Matt Sarett03dd6d52017-01-23 12:15:09 -0500691 // This can still fail, most notably in the case of a invalid color type or alpha type
692 // conversion. We could pull those checks into this function and avoid the unnecessary
693 // generation ID bump. But then we would be performing those checks twice, since they
694 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400695 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000696}
reed@google.com51df9e32010-12-23 19:29:18 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698//////////////////////////////////////////////////////////////////////////////
699
reed2ff1fce2014-12-11 07:07:37 -0800700void SkCanvas::checkForDeferredSave() {
701 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800702 this->doSave();
703 }
704}
705
reedf0090cb2014-11-26 08:55:51 -0800706int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800707#ifdef SK_DEBUG
708 int count = 0;
709 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
710 for (;;) {
711 const MCRec* rec = (const MCRec*)iter.next();
712 if (!rec) {
713 break;
714 }
715 count += 1 + rec->fDeferredSaveCount;
716 }
717 SkASSERT(count == fSaveCount);
718#endif
719 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800720}
721
722int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800723 fSaveCount += 1;
724 fMCRec->fDeferredSaveCount += 1;
725 return this->getSaveCount() - 1; // return our prev value
726}
727
728void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800729 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700730
731 SkASSERT(fMCRec->fDeferredSaveCount > 0);
732 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800733 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800734}
735
736void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800737 if (fMCRec->fDeferredSaveCount > 0) {
738 SkASSERT(fSaveCount > 1);
739 fSaveCount -= 1;
740 fMCRec->fDeferredSaveCount -= 1;
741 } else {
742 // check for underflow
743 if (fMCStack.count() > 1) {
744 this->willRestore();
745 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700746 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800747 this->internalRestore();
748 this->didRestore();
749 }
reedf0090cb2014-11-26 08:55:51 -0800750 }
751}
752
753void SkCanvas::restoreToCount(int count) {
754 // sanity check
755 if (count < 1) {
756 count = 1;
757 }
mtkleinf0f14112014-12-12 08:46:25 -0800758
reedf0090cb2014-11-26 08:55:51 -0800759 int n = this->getSaveCount() - count;
760 for (int i = 0; i < n; ++i) {
761 this->restore();
762 }
763}
764
reed2ff1fce2014-12-11 07:07:37 -0800765void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700767 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000769
Mike Reedc42a1cd2017-02-14 14:25:14 -0500770 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771}
772
reed4960eee2015-12-18 07:09:18 -0800773bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400774 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775}
776
reed4960eee2015-12-18 07:09:18 -0800777bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700778 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400779 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
780 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
781 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
782 // filter does not require a decomposed CTM matrix, the filter space and device space are the
783 // same. When it has been decomposed, we want the original image filter node to process the
784 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
785 // longer the original filter, but has the remainder matrix baked into it, and passing in the
786 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
787 // to the original filter node (barring inflation from consecutive calls to mapRect). While
788 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
789 // passing getDeviceClipBounds() to 'imageFilter' is correct.
790 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
791 // be important to more accurately calculate the clip bounds in the layer space for the original
792 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500793 SkIRect clipBounds = this->getDeviceClipBounds();
794 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000795 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000796 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000797
reed96e657d2015-03-10 17:30:07 -0700798 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
799
Robert Phillips12078432018-05-17 11:17:39 -0400800 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
801 // If the image filter DAG affects transparent black then we will need to render
802 // out to the clip bounds
803 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000804 }
Robert Phillips12078432018-05-17 11:17:39 -0400805
806 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700807 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700809 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400810 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400812 inputSaveLayerBounds = clipBounds;
813 }
814
815 if (imageFilter) {
816 // expand the clip bounds by the image filter DAG to include extra content that might
817 // be required by the image filters.
818 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
819 SkImageFilter::kReverse_MapDirection,
820 &inputSaveLayerBounds);
821 }
822
823 SkIRect clippedSaveLayerBounds;
824 if (bounds) {
825 // For better or for worse, user bounds currently act as a hard clip on the layer's
826 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
827 clippedSaveLayerBounds = inputSaveLayerBounds;
828 } else {
829 // If there are no user bounds, we don't want to artificially restrict the resulting
830 // layer bounds, so allow the expanded clip bounds free reign.
831 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800833
834 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400835 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800836 if (BoundsAffectsClip(saveLayerFlags)) {
837 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
838 fMCRec->fRasterClip.setEmpty();
839 fDeviceClipBounds.setEmpty();
840 }
841 return false;
842 }
Robert Phillips12078432018-05-17 11:17:39 -0400843 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844
reed4960eee2015-12-18 07:09:18 -0800845 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700846 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400847 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
848 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000849 }
850
851 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400852 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000853 }
Robert Phillips12078432018-05-17 11:17:39 -0400854
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 return true;
856}
857
reed4960eee2015-12-18 07:09:18 -0800858int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
859 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000860}
861
Cary Clarke041e312018-03-06 13:00:52 -0500862int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700863 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400864 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
865 // no need for the layer (or any of the draws until the matching restore()
866 this->save();
867 this->clipRect({0,0,0,0});
868 } else {
869 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
870 fSaveCount += 1;
871 this->internalSaveLayer(rec, strategy);
872 }
reed4960eee2015-12-18 07:09:18 -0800873 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800874}
875
Mike Reed148b7fd2018-12-18 17:38:18 -0500876int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
877 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
878 // Assuming clips never expand, if the request bounds is outside of the current clip
879 // there is no need to copy/restore the area, so just devolve back to a regular save.
880 this->save();
881 } else {
882 bool doTheWork = this->onDoSaveBehind(bounds);
883 fSaveCount += 1;
884 this->internalSave();
885 if (doTheWork) {
886 this->internalSaveBehind(bounds);
887 }
888 }
889 return this->getSaveCount() - 1;
890}
891
reeda2217ef2016-07-20 06:04:34 -0700892void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500893 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500894 const SkMatrix& ctm) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400895 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
896 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400897 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig915b7792019-10-22 17:40:41 +0000898
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400899 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400900 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
901 // This means that we only have to copy a dst-sized block of pixels out of src and translate
902 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400903 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
904 dstOrigin.y() - src->getOrigin().y(),
905 dst->width(), dst->height());
906 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400907 return;
908 }
909
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400910 auto special = src->snapSpecial(snapBounds);
911 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400912 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
913 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400914 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
915 }
916 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400917 }
918
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400919 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
920 // by the backdrop filter.
921 SkMatrix toRoot, layerMatrix;
922 SkSize scale;
923 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
924 toRoot = SkMatrix::I();
925 layerMatrix = ctm;
926 } else if (ctm.decomposeScale(&scale, &toRoot)) {
927 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
928 } else {
929 // Perspective, for now, do no scaling of the layer itself.
930 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
931 // the matrix, e.g. based on the midpoint of the near/far planes?
932 toRoot = ctm;
933 layerMatrix = SkMatrix::I();
934 }
935
936 // We have to map the dst bounds from the root space into the layer space where filtering will
937 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
938 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
939 // is a safe, conservative estimate.
940 SkMatrix fromRoot;
941 if (!toRoot.invert(&fromRoot)) {
942 return;
943 }
944
945 // This represents what the backdrop filter needs to produce in the layer space, and is sized
946 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
947 SkIRect layerTargetBounds = fromRoot.mapRect(
948 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
949 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
950 // require some extra input pixels.
951 SkIRect layerInputBounds = filter->filterBounds(
952 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
953 &layerTargetBounds);
954
955 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400956 // be the conservative contents required to fill a layerInputBounds-sized surface with the
957 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400958 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
959 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
960 if (!backdropBounds.intersect(srcDevRect)) {
961 return;
962 }
963
964 auto special = src->snapSpecial(backdropBounds);
965 if (!special) {
966 return;
967 }
968
969 SkColorType colorType = src->imageInfo().colorType();
970 if (colorType == kUnknown_SkColorType) {
971 colorType = kRGBA_8888_SkColorType;
972 }
973 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400974
975 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400976 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400977 // Drawing the temporary and final filtered image requires a higher filter quality if the
978 // 'toRoot' transformation is not identity, in order to minimize the impact on already
979 // rendered edges/content.
980 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
981 p.setFilterQuality(kHigh_SkFilterQuality);
982
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400983 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
984 // and stored in a temporary surface, which is then used as the input to the actual filter.
985 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
986 if (!tmpSurface) {
987 return;
988 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400989
990 auto tmpCanvas = tmpSurface->getCanvas();
991 tmpCanvas->clear(SK_ColorTRANSPARENT);
992 // Reading in reverse, this takes the backdrop bounds from src device space into the root
993 // space, then maps from root space into the layer space, then maps it so the input layer's
994 // top left corner is (0, 0). This transformation automatically accounts for any cropping
995 // performed on backdropBounds.
996 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
997 tmpCanvas->concat(fromRoot);
998 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -0400999
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001000 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
1001 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
1002 special = tmpSurface->makeImageSnapshot();
1003 } else {
1004 // Since there is no extra transform that was done, update the input bounds to reflect
1005 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
1006 // was equal to backdropBounds before it was made relative to the src device and cropped.
1007 // When we use the original snapped image directly, just map the update backdrop bounds
1008 // back into the shared layer space
1009 layerInputBounds = backdropBounds;
1010 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001011
1012 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1013 // draw will be 1-1 so there is no need to increase filter quality.
1014 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001015 }
1016
1017 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1018 // layer space. This has to further offset everything so that filter evaluation thinks the
1019 // source image's top left corner is (0, 0).
1020 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1021 // this can be simplified.
1022 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1023 SkMatrix filterCTM = layerMatrix;
1024 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1025 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1026
1027 SkIPoint offset;
1028 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001029 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001030 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1031 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1032 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1033 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001034 offset += layerInputBounds.topLeft();
1035
1036 // Manually setting the device's CTM requires accounting for the device's origin.
1037 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001038 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001039 // a global CTM instead of a device CTM.
1040 SkMatrix dstCTM = toRoot;
1041 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001042 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001043
1044 // And because devices don't have a special-image draw function that supports arbitrary
1045 // matrices, we are abusing the asImage() functionality here...
1046 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001047 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001048 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001049 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001050 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1051 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001052 }
robertphillips7354a4b2015-12-16 05:08:27 -08001053}
reed70ee31b2015-12-10 13:44:45 -08001054
Mike Kleine083f7c2018-02-07 12:54:27 -05001055static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001056 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001057 if (prev.bytesPerPixel() <= 4 &&
1058 prev.colorType() != kRGBA_8888_SkColorType &&
1059 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001060 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1061 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1062 ct = kN32_SkColorType;
1063 }
1064 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001065}
1066
reed4960eee2015-12-18 07:09:18 -08001067void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001068 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001069 const SkRect* bounds = rec.fBounds;
1070 const SkPaint* paint = rec.fPaint;
1071 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1072
Mike Reed5532c2a2019-02-23 12:00:32 -05001073 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1074 // regardless of any hint-rect from the caller. skbug.com/8783
1075 if (rec.fBackdrop) {
1076 bounds = nullptr;
1077 }
1078
reed8c30a812016-04-20 16:36:51 -07001079 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001080 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001081 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001082 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001083
reed8c30a812016-04-20 16:36:51 -07001084 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001085 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1086 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1087 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001088 *
1089 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001090 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1091 * if necessary.
1092 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1093 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001094 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
Michael Ludwig08b260c2019-05-17 11:21:53 -04001095 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001096 * of the original imagefilter, and draw that (via drawSprite)
1097 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1098 *
1099 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1100 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1101 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001102 if (imageFilter) {
1103 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001104 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1105 &modifiedCTM);
1106 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001107 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001108 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001109 modifiedRec = fMCRec;
1110 this->internalSetMatrix(modifiedCTM);
1111 SkPaint* p = lazyP.set(*paint);
1112 p->setImageFilter(std::move(modifiedFilter));
1113 imageFilter = p->getImageFilter();
1114 paint = p;
1115 }
1116 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1117 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001118 }
reed8c30a812016-04-20 16:36:51 -07001119
junov@chromium.orga907ac32012-02-24 21:54:07 +00001120 // do this before we create the layer. We don't call the public save() since
1121 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001122 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001123
junov@chromium.orga907ac32012-02-24 21:54:07 +00001124 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001125 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001126 if (modifiedRec) {
1127 // In this case there will be no layer in which to stash the matrix so we need to
1128 // revert the prior MCRec to its earlier state.
1129 modifiedRec->fMatrix = stashedMatrix;
1130 }
reed2ff1fce2014-12-11 07:07:37 -08001131 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 }
1133
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001134 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1135 // the clipRectBounds() call above?
1136 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001137 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001138 }
1139
reed8dc0ccb2015-03-20 06:32:52 -07001140 SkPixelGeometry geo = fProps.pixelGeometry();
1141 if (paint) {
reed76033be2015-03-14 10:54:31 -07001142 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001143 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001144 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001145 }
1146 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147
robertphillips5139e502016-07-19 05:10:40 -07001148 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001149 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001150 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001151 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001152 }
reedb2db8982014-11-13 12:41:02 -08001153
Mike Kleine083f7c2018-02-07 12:54:27 -05001154 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001155 if (rec.fSaveLayerFlags & kF16ColorType) {
1156 info = info.makeColorType(kRGBA_F16_SkColorType);
1157 }
reed129ed1c2016-02-22 06:42:31 -08001158
Hal Canary704cd322016-11-07 14:13:52 -05001159 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001160 {
Florin Malita4571e492019-07-16 10:25:58 -04001161 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001162 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001163 const bool trackCoverage =
1164 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001165 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001166 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001167 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001168 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1169 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001170 return;
reed61f501f2015-04-29 08:34:00 -07001171 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001172 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001173 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174
Mike Reedb43a3e02017-02-11 10:18:58 -05001175 // only have a "next" if this new layer doesn't affect the clip (rare)
1176 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 fMCRec->fLayer = layer;
1178 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001179
Mike Reedc61abee2017-02-28 17:45:27 -05001180 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001181 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001182 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001183 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001184
Mike Reedc42a1cd2017-02-14 14:25:14 -05001185 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1186
1187 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1188 if (layer->fNext) {
1189 // need to punch a hole in the previous device, so we don't draw there, given that
1190 // the new top-layer will allow drawing to happen "below" it.
1191 SkRegion hole(ir);
1192 do {
1193 layer = layer->fNext;
1194 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1195 } while (layer->fNext);
1196 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001199int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001200 if (0xFF == alpha) {
1201 return this->saveLayer(bounds, nullptr);
1202 } else {
1203 SkPaint tmpPaint;
1204 tmpPaint.setAlpha(alpha);
1205 return this->saveLayer(bounds, &tmpPaint);
1206 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001207}
1208
Mike Reed148b7fd2018-12-18 17:38:18 -05001209void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001210 SkIRect devBounds;
1211 if (localBounds) {
1212 SkRect tmp;
1213 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1214 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1215 devBounds.setEmpty();
1216 }
1217 } else {
1218 devBounds = this->getDeviceClipBounds();
1219 }
1220 if (devBounds.isEmpty()) {
1221 return;
1222 }
1223
Mike Reed148b7fd2018-12-18 17:38:18 -05001224 SkBaseDevice* device = this->getTopDevice();
1225 if (nullptr == device) { // Do we still need this check???
1226 return;
1227 }
1228
Michael Ludwig915b7792019-10-22 17:40:41 +00001229 // need the bounds relative to the device itself
1230 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
Mike Reed148b7fd2018-12-18 17:38:18 -05001231
Michael Ludwigac352122019-08-28 21:03:05 +00001232 // This is getting the special image from the current device, which is then drawn into (both by
1233 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1234 // own device, we need to explicitly copy the back image contents so that its original content
1235 // is available when we splat it back later during restore.
1236 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001237 if (!backImage) {
1238 return;
1239 }
1240
1241 // we really need the save, so we can wack the fMCRec
1242 this->checkForDeferredSave();
1243
1244 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1245
1246 SkPaint paint;
1247 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001248 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001249}
1250
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251void SkCanvas::internalRestore() {
1252 SkASSERT(fMCStack.count() != 0);
1253
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001254 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 DeviceCM* layer = fMCRec->fLayer; // may be null
1256 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001257 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258
Mike Reed148b7fd2018-12-18 17:38:18 -05001259 // move this out before we do the actual restore
1260 auto backImage = std::move(fMCRec->fBackImage);
1261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 // now do the normal restore()
1263 fMCRec->~MCRec(); // balanced in save()
1264 fMCStack.pop_back();
1265 fMCRec = (MCRec*)fMCStack.back();
1266
Mike Reedc42a1cd2017-02-14 14:25:14 -05001267 if (fMCRec) {
1268 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1269 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001270
Mike Reed148b7fd2018-12-18 17:38:18 -05001271 if (backImage) {
1272 SkPaint paint;
1273 paint.setBlendMode(SkBlendMode::kDstOver);
1274 const int x = backImage->fLoc.x();
1275 const int y = backImage->fLoc.y();
1276 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1277 nullptr, SkMatrix::I());
1278 }
1279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1281 since if we're being recorded, we don't want to record this (the
1282 recorder will have already recorded the restore).
1283 */
bsalomon49f085d2014-09-05 13:34:00 -07001284 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001285 if (fMCRec) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001286 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001287 layer->fDevice->setImmutable();
Michael Ludwig915b7792019-10-22 17:40:41 +00001288 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
1289 layer->fPaint.get(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001290 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001291 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001292 this->internalSetMatrix(layer->fStashedMatrix);
Michael Ludwig915b7792019-10-22 17:40:41 +00001293 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001294 delete layer;
reedb679ca82015-04-07 04:40:48 -07001295 } else {
1296 // we're at the root
reeda499f902015-05-01 09:34:31 -07001297 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001298 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001299 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001301 }
msarettfbfa2582016-08-12 08:29:08 -07001302
1303 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001304 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001305 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1306 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307}
1308
reede8f30622016-03-23 18:59:25 -07001309sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001310 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001311 props = &fProps;
1312 }
1313 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001314}
1315
reede8f30622016-03-23 18:59:25 -07001316sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001317 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001318 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001319}
1320
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001321SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001322 return this->onImageInfo();
1323}
1324
1325SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001326 SkBaseDevice* dev = this->getDevice();
1327 if (dev) {
1328 return dev->imageInfo();
1329 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001330 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001331 }
1332}
1333
brianosman898235c2016-04-06 07:38:23 -07001334bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001335 return this->onGetProps(props);
1336}
1337
1338bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001339 SkBaseDevice* dev = this->getDevice();
1340 if (dev) {
1341 if (props) {
1342 *props = fProps;
1343 }
1344 return true;
1345 } else {
1346 return false;
1347 }
1348}
1349
reed6ceeebd2016-03-09 14:26:26 -08001350bool SkCanvas::peekPixels(SkPixmap* pmap) {
1351 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001352}
1353
reed884e97c2015-05-26 11:31:54 -07001354bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001355 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001356 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001357}
1358
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001359void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001360 SkPixmap pmap;
1361 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001362 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001363 }
1364 if (info) {
1365 *info = pmap.info();
1366 }
1367 if (rowBytes) {
1368 *rowBytes = pmap.rowBytes();
1369 }
1370 if (origin) {
Michael Ludwig915b7792019-10-22 17:40:41 +00001371 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001372 }
reed884e97c2015-05-26 11:31:54 -07001373 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001374}
1375
reed884e97c2015-05-26 11:31:54 -07001376bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001377 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001378 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001379}
1380
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382
Mike Reed8bcd1282019-03-13 16:51:54 -04001383// In our current design/features, we should never have a layer (src) in a different colorspace
1384// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1385// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1386// colorspace.
1387static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1388 SkASSERT(src == dst);
1389}
1390
Michael Ludwig915b7792019-10-22 17:40:41 +00001391void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
Florin Malita53f77bd2017-04-28 13:48:37 -04001392 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001394 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 paint = &tmp;
1396 }
reed@google.com4b226022011-01-11 18:32:13 +00001397
Mike Reed38992392019-07-30 10:48:15 -04001398 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001401 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001402 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1403 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001404 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001405 SkImageFilter* filter = paint->getImageFilter();
Michael Ludwig915b7792019-10-22 17:40:41 +00001406 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001407 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001408 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1409 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001410 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1411 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001412 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1413 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001414 }
reed@google.com76dd2772012-01-05 21:15:07 +00001415 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001416 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001417 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 }
reeda2217ef2016-07-20 06:04:34 -07001419
Mike Reed38992392019-07-30 10:48:15 -04001420 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421}
1422
reed32704672015-12-16 08:27:10 -08001423/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001424
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001425void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001426 if (dx || dy) {
1427 this->checkForDeferredSave();
reedfe69b502016-09-12 06:31:48 -07001428 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001429
reedfe69b502016-09-12 06:31:48 -07001430 // Translate shouldn't affect the is-scale-translateness of the matrix.
1431 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001432
Mike Reedc42a1cd2017-02-14 14:25:14 -05001433 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001434
reedfe69b502016-09-12 06:31:48 -07001435 this->didTranslate(dx,dy);
1436 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437}
1438
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001439void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001440 SkMatrix m;
1441 m.setScale(sx, sy);
1442 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443}
1444
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001445void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001446 SkMatrix m;
1447 m.setRotate(degrees);
1448 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
bungeman7438bfc2016-07-12 15:01:19 -07001451void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1452 SkMatrix m;
1453 m.setRotate(degrees, px, py);
1454 this->concat(m);
1455}
1456
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001457void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001458 SkMatrix m;
1459 m.setSkew(sx, sy);
1460 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001461}
1462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001464 if (matrix.isIdentity()) {
1465 return;
1466 }
1467
reed2ff1fce2014-12-11 07:07:37 -08001468 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001469 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001470 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001471
Mike Reed7627fa52017-02-08 10:07:53 -05001472 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001473
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001474 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001475}
1476
reed8c30a812016-04-20 16:36:51 -07001477void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001478 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001479 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001480
Mike Reedc42a1cd2017-02-14 14:25:14 -05001481 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001482}
1483
1484void SkCanvas::setMatrix(const SkMatrix& matrix) {
1485 this->checkForDeferredSave();
1486 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001487 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488}
1489
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001491 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492}
1493
1494//////////////////////////////////////////////////////////////////////////////
1495
Mike Reedc1f77742016-12-09 09:00:50 -05001496void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001497 if (!rect.isFinite()) {
1498 return;
1499 }
reed2ff1fce2014-12-11 07:07:37 -08001500 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001501 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1502 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001503}
1504
Mike Reedc1f77742016-12-09 09:00:50 -05001505void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001506 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001507
Mike Reed7627fa52017-02-08 10:07:53 -05001508 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001509
reedc64eff52015-11-21 12:39:45 -08001510 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001511 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1512 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001513 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001516void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1517 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001518 if (fClipRestrictionRect.isEmpty()) {
1519 // we notify the device, but we *dont* resolve deferred saves (since we're just
1520 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001521 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001522 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001523 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001524 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001525 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001526 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001527 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1528 }
1529}
1530
Mike Reedc1f77742016-12-09 09:00:50 -05001531void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001532 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001533 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001534 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001535 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1536 } else {
1537 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001538 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001539}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001540
Mike Reedc1f77742016-12-09 09:00:50 -05001541void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001542 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001543
Brian Salomona3b45d42016-10-03 11:36:16 -04001544 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001545
Mike Reed7627fa52017-02-08 10:07:53 -05001546 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001547
Mike Reed20800c82017-11-15 16:09:04 -05001548 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1549 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001550 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001551}
1552
Mike Reedc1f77742016-12-09 09:00:50 -05001553void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001554 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001555 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001556
1557 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1558 SkRect r;
1559 if (path.isRect(&r)) {
1560 this->onClipRect(r, op, edgeStyle);
1561 return;
1562 }
1563 SkRRect rrect;
1564 if (path.isOval(&r)) {
1565 rrect.setOval(r);
1566 this->onClipRRect(rrect, op, edgeStyle);
1567 return;
1568 }
1569 if (path.isRRect(&rrect)) {
1570 this->onClipRRect(rrect, op, edgeStyle);
1571 return;
1572 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001573 }
robertphillips39f05382015-11-24 09:30:12 -08001574
1575 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001576}
1577
Mike Reedc1f77742016-12-09 09:00:50 -05001578void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001579 AutoValidateClip avc(this);
1580
Brian Salomona3b45d42016-10-03 11:36:16 -04001581 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001582
Mike Reed7627fa52017-02-08 10:07:53 -05001583 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584
Brian Salomona3b45d42016-10-03 11:36:16 -04001585 const SkPath* rasterClipPath = &path;
1586 const SkMatrix* matrix = &fMCRec->fMatrix;
Mike Reed20800c82017-11-15 16:09:04 -05001587 fMCRec->fRasterClip.opPath(*rasterClipPath, *matrix, this->getTopLayerBounds(),
1588 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001589 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590}
1591
Mike Reedc1f77742016-12-09 09:00:50 -05001592void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001593 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595}
1596
Mike Reedc1f77742016-12-09 09:00:50 -05001597void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001598 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001599
reed@google.com5c3d1472011-02-22 19:12:23 +00001600 AutoValidateClip avc(this);
1601
Mike Reed20800c82017-11-15 16:09:04 -05001602 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001603 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604}
1605
reed@google.com819c9212011-02-23 18:56:55 +00001606#ifdef SK_DEBUG
1607void SkCanvas::validateClip() const {
1608 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001609 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001610 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001611 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001612 return;
1613 }
reed@google.com819c9212011-02-23 18:56:55 +00001614}
1615#endif
1616
Mike Reeda1361362017-03-07 09:37:29 -05001617bool SkCanvas::androidFramework_isClipAA() const {
1618 bool containsAA = false;
1619
1620 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1621
1622 return containsAA;
1623}
1624
1625class RgnAccumulator {
1626 SkRegion* fRgn;
1627public:
1628 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1629 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1630 SkIPoint origin = device->getOrigin();
1631 if (origin.x() | origin.y()) {
1632 rgn->translate(origin.x(), origin.y());
1633 }
1634 fRgn->op(*rgn, SkRegion::kUnion_Op);
1635 }
1636};
1637
1638void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1639 RgnAccumulator accum(rgn);
1640 SkRegion tmp;
1641
1642 rgn->setEmpty();
1643 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001644}
1645
reed@google.com5c3d1472011-02-22 19:12:23 +00001646///////////////////////////////////////////////////////////////////////////////
1647
reed@google.com754de5f2014-02-24 19:38:20 +00001648bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001649 return fMCRec->fRasterClip.isEmpty();
1650
1651 // TODO: should we only use the conservative answer in a recording canvas?
1652#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001653 SkBaseDevice* dev = this->getTopDevice();
1654 // if no device we return true
1655 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001656#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001657}
1658
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001659bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001660 SkBaseDevice* dev = this->getTopDevice();
1661 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001662 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001663}
1664
msarettfbfa2582016-08-12 08:29:08 -07001665static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1666#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1667 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1668 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1669 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1670 return 0xF != _mm_movemask_ps(mask);
1671#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1672 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1673 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1674 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1675 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1676#else
1677 SkRect devRectAsRect;
1678 SkRect devClipAsRect;
1679 devRect.store(&devRectAsRect.fLeft);
1680 devClip.store(&devClipAsRect.fLeft);
1681 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1682#endif
1683}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001684
msarettfbfa2582016-08-12 08:29:08 -07001685// It's important for this function to not be inlined. Otherwise the compiler will share code
1686// between the fast path and the slow path, resulting in two slow paths.
1687static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1688 const SkMatrix& matrix) {
1689 SkRect deviceRect;
1690 matrix.mapRect(&deviceRect, src);
1691 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1692}
1693
1694bool SkCanvas::quickReject(const SkRect& src) const {
1695#ifdef SK_DEBUG
1696 // Verify that fDeviceClipBounds are set properly.
1697 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001698 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001699 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001700 } else {
msarettfbfa2582016-08-12 08:29:08 -07001701 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 }
msarettfbfa2582016-08-12 08:29:08 -07001703
msarett9637ea92016-08-18 14:03:30 -07001704 // Verify that fIsScaleTranslate is set properly.
1705 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001706#endif
1707
msarett9637ea92016-08-18 14:03:30 -07001708 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001709 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1710 }
1711
1712 // We inline the implementation of mapScaleTranslate() for the fast path.
1713 float sx = fMCRec->fMatrix.getScaleX();
1714 float sy = fMCRec->fMatrix.getScaleY();
1715 float tx = fMCRec->fMatrix.getTranslateX();
1716 float ty = fMCRec->fMatrix.getTranslateY();
1717 Sk4f scale(sx, sy, sx, sy);
1718 Sk4f trans(tx, ty, tx, ty);
1719
1720 // Apply matrix.
1721 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1722
1723 // Make sure left < right, top < bottom.
1724 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1725 Sk4f min = Sk4f::Min(ltrb, rblt);
1726 Sk4f max = Sk4f::Max(ltrb, rblt);
1727 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1728 // ARM this sequence generates the fastest (a single instruction).
1729 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1730
1731 // Check if the device rect is NaN or outside the clip.
1732 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733}
1734
reed@google.com3b3e8952012-08-16 20:53:31 +00001735bool SkCanvas::quickReject(const SkPath& path) const {
1736 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737}
1738
Mike Klein83c8dd92017-11-28 17:08:45 -05001739SkRect SkCanvas::getLocalClipBounds() const {
1740 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001741 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001742 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743 }
1744
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001745 SkMatrix inverse;
1746 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001747 if (!fMCRec->fMatrix.invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001748 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001749 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750
Mike Reed42e8c532017-01-23 14:09:13 -05001751 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001752 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001753 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001754
Mike Reedb57b9312018-04-23 12:12:54 -04001755 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001756 inverse.mapRect(&bounds, r);
1757 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758}
1759
Mike Klein83c8dd92017-11-28 17:08:45 -05001760SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001761 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001762}
1763
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001765 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766}
1767
Brian Osman11052242016-10-27 14:47:55 -04001768GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001769 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001770 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001771}
1772
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001773GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001774 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001775 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001776}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001777
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001778void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1779 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001780 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001781 if (outer.isEmpty()) {
1782 return;
1783 }
1784 if (inner.isEmpty()) {
1785 this->drawRRect(outer, paint);
1786 return;
1787 }
1788
1789 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001790 // be able to return ...
1791 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001792 //
1793 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001794 if (!outer.getBounds().contains(inner.getBounds())) {
1795 return;
1796 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001797
1798 this->onDrawDRRect(outer, inner, paint);
1799}
1800
reed41af9662015-01-05 07:49:08 -08001801void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001802 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001803 this->onDrawPaint(paint);
1804}
1805
1806void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001807 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001808 // To avoid redundant logic in our culling code and various backends, we always sort rects
1809 // before passing them along.
1810 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001811}
1812
Mike Reedd5674082019-04-19 15:00:47 -04001813void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1814 TRACE_EVENT0("skia", TRACE_FUNC);
1815 this->onDrawBehind(paint);
1816}
1817
msarettdca352e2016-08-26 06:37:45 -07001818void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001819 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001820 if (region.isEmpty()) {
1821 return;
1822 }
1823
1824 if (region.isRect()) {
1825 return this->drawIRect(region.getBounds(), paint);
1826 }
1827
1828 this->onDrawRegion(region, paint);
1829}
1830
reed41af9662015-01-05 07:49:08 -08001831void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001832 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001833 // To avoid redundant logic in our culling code and various backends, we always sort rects
1834 // before passing them along.
1835 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001836}
1837
1838void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001839 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001840 this->onDrawRRect(rrect, paint);
1841}
1842
1843void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001844 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001845 this->onDrawPoints(mode, count, pts, paint);
1846}
1847
Mike Reede88a1cb2017-03-17 09:50:46 -04001848void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1849 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001850 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001851 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001852 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1853 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001854 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001855}
1856
1857void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001858 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001859 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001860 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1861}
1862
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001863void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1864 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001865 TRACE_EVENT0("skia", TRACE_FUNC);
1866 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001867 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001868 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1869}
1870
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001871void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1872 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001873 TRACE_EVENT0("skia", TRACE_FUNC);
1874 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001875 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001876 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001877}
1878
1879void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001880 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001881 this->onDrawPath(path, paint);
1882}
1883
reeda85d4d02015-05-06 12:56:48 -07001884void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001885 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001886 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001887 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001888}
1889
Mike Reedc4e31092018-01-30 11:15:27 -05001890// Returns true if the rect can be "filled" : non-empty and finite
1891static bool fillable(const SkRect& r) {
1892 SkScalar w = r.width();
1893 SkScalar h = r.height();
1894 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1895}
1896
reede47829b2015-08-06 10:02:53 -07001897void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1898 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001899 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001900 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001901 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001902 return;
1903 }
1904 this->onDrawImageRect(image, &src, dst, paint, constraint);
1905}
reed41af9662015-01-05 07:49:08 -08001906
reed84984ef2015-07-17 07:09:43 -07001907void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1908 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001909 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001910 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001911}
1912
Brian Salomonf08002c2018-10-26 16:15:46 -04001913void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001914 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001915 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001916 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001917}
reede47829b2015-08-06 10:02:53 -07001918
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001919namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001920class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001921public:
Brian Salomon969be1c2018-05-21 14:37:49 -04001922 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
1923 if (!origPaint) {
1924 return;
1925 }
1926 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
1927 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
1928 }
1929 if (origPaint->getMaskFilter()) {
1930 fPaint.writable()->setMaskFilter(nullptr);
1931 }
1932 if (origPaint->isAntiAlias()) {
1933 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001934 }
1935 }
1936
1937 const SkPaint* get() const {
1938 return fPaint;
1939 }
1940
1941private:
Brian Salomon969be1c2018-05-21 14:37:49 -04001942 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001943};
1944} // namespace
1945
reed4c21dc52015-06-25 12:32:03 -07001946void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1947 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001948 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001949 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001950 if (dst.isEmpty()) {
1951 return;
1952 }
msarett552bca92016-08-03 06:53:26 -07001953 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001954 LatticePaint latticePaint(paint);
1955 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07001956 } else {
reede47829b2015-08-06 10:02:53 -07001957 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001958 }
reed4c21dc52015-06-25 12:32:03 -07001959}
1960
msarett16882062016-08-16 09:31:08 -07001961void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1962 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001963 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07001964 RETURN_ON_NULL(image);
1965 if (dst.isEmpty()) {
1966 return;
1967 }
msarett71df2d72016-09-30 12:41:42 -07001968
1969 SkIRect bounds;
1970 Lattice latticePlusBounds = lattice;
1971 if (!latticePlusBounds.fBounds) {
1972 bounds = SkIRect::MakeWH(image->width(), image->height());
1973 latticePlusBounds.fBounds = &bounds;
1974 }
1975
1976 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001977 LatticePaint latticePaint(paint);
1978 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07001979 } else {
1980 this->drawImageRect(image, dst, paint);
1981 }
1982}
1983
reed41af9662015-01-05 07:49:08 -08001984void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001985 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07001986 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001987 return;
1988 }
reed41af9662015-01-05 07:49:08 -08001989 this->onDrawBitmap(bitmap, dx, dy, paint);
1990}
1991
reede47829b2015-08-06 10:02:53 -07001992void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001993 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001994 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07001995 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001996 return;
1997 }
reede47829b2015-08-06 10:02:53 -07001998 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001999}
2000
reed84984ef2015-07-17 07:09:43 -07002001void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2002 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002003 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002004}
2005
reede47829b2015-08-06 10:02:53 -07002006void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2007 SrcRectConstraint constraint) {
2008 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2009 constraint);
2010}
reede47829b2015-08-06 10:02:53 -07002011
reed41af9662015-01-05 07:49:08 -08002012void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2013 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002014 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002015 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002016 return;
2017 }
msarett552bca92016-08-03 06:53:26 -07002018 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002019 LatticePaint latticePaint(paint);
2020 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002021 } else {
reeda5517e22015-07-14 10:54:12 -07002022 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002023 }
reed41af9662015-01-05 07:49:08 -08002024}
2025
msarettc573a402016-08-02 08:05:56 -07002026void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2027 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002028 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002029 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002030 return;
2031 }
msarett71df2d72016-09-30 12:41:42 -07002032
2033 SkIRect bounds;
2034 Lattice latticePlusBounds = lattice;
2035 if (!latticePlusBounds.fBounds) {
2036 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2037 latticePlusBounds.fBounds = &bounds;
2038 }
2039
2040 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002041 LatticePaint latticePaint(paint);
2042 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002043 } else {
msarett16882062016-08-16 09:31:08 -07002044 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002045 }
msarettc573a402016-08-02 08:05:56 -07002046}
2047
reed71c3c762015-06-24 10:29:17 -07002048void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002049 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002050 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002051 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002052 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002053 if (count <= 0) {
2054 return;
2055 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002056 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002057 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002058 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002059}
2060
reedf70b5312016-03-04 16:36:20 -08002061void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002062 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002063 if (key) {
2064 this->onDrawAnnotation(rect, key, value);
2065 }
2066}
2067
reede47829b2015-08-06 10:02:53 -07002068void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2069 const SkPaint* paint, SrcRectConstraint constraint) {
2070 if (src) {
2071 this->drawImageRect(image, *src, dst, paint, constraint);
2072 } else {
2073 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2074 dst, paint, constraint);
2075 }
2076}
2077void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2078 const SkPaint* paint, SrcRectConstraint constraint) {
2079 if (src) {
2080 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2081 } else {
2082 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2083 dst, paint, constraint);
2084 }
2085}
2086
Mike Reed4204da22017-05-17 08:53:36 -04002087void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002088 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002089 this->onDrawShadowRec(path, rec);
2090}
2091
2092void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2093 SkPaint paint;
2094 const SkRect& pathBounds = path.getBounds();
2095
Mike Reed38992392019-07-30 10:48:15 -04002096 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002097 while (iter.next()) {
2098 iter.fDevice->drawShadow(path, rec);
2099 }
Mike Reed38992392019-07-30 10:48:15 -04002100 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002101}
2102
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002103void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002104 QuadAAFlags aaFlags, const SkColor4f& color,
2105 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002106 TRACE_EVENT0("skia", TRACE_FUNC);
2107 // Make sure the rect is sorted before passing it along
2108 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2109}
2110
2111void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2112 const SkPoint dstClips[],
2113 const SkMatrix preViewMatrices[],
2114 const SkPaint* paint,
2115 SrcRectConstraint constraint) {
2116 TRACE_EVENT0("skia", TRACE_FUNC);
2117 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2118}
2119
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120//////////////////////////////////////////////////////////////////////////////
2121// These are the virtual drawing methods
2122//////////////////////////////////////////////////////////////////////////////
2123
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002124void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002125 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002126 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2127 }
2128}
2129
reed41af9662015-01-05 07:49:08 -08002130void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002131 this->internalDrawPaint(paint);
2132}
2133
2134void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002135 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136
2137 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002138 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139 }
2140
Mike Reed38992392019-07-30 10:48:15 -04002141 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142}
2143
reed41af9662015-01-05 07:49:08 -08002144void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2145 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146 if ((long)count <= 0) {
2147 return;
2148 }
2149
Mike Reed822128b2017-02-28 16:41:03 -05002150 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002151 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002152 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002153 // special-case 2 points (common for drawing a single line)
2154 if (2 == count) {
2155 r.set(pts[0], pts[1]);
2156 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002157 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002158 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002159 if (!r.isFinite()) {
2160 return;
2161 }
Mike Reed822128b2017-02-28 16:41:03 -05002162 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002163 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2164 return;
2165 }
2166 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002167 }
reed@google.coma584aed2012-05-16 14:06:02 +00002168
halcanary96fcdcc2015-08-27 07:41:13 -07002169 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170
Mike Reed38992392019-07-30 10:48:15 -04002171 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002172
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002174 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175 }
reed@google.com4b226022011-01-11 18:32:13 +00002176
Mike Reed38992392019-07-30 10:48:15 -04002177 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178}
2179
reed4a167172016-08-18 17:15:25 -07002180static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002181 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002182}
2183
reed41af9662015-01-05 07:49:08 -08002184void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002185 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002187 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002188 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002189 return;
2190 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 }
reed@google.com4b226022011-01-11 18:32:13 +00002192
reed4a167172016-08-18 17:15:25 -07002193 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002194 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195
reed4a167172016-08-18 17:15:25 -07002196 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002197 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002198 }
2199
Mike Reed38992392019-07-30 10:48:15 -04002200 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002201 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002202 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002203 SkDrawIter iter(this);
2204 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002205 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002206 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208}
2209
msarett44df6512016-08-25 13:54:30 -07002210void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002211 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002212 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002213 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002214 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2215 return;
2216 }
msarett44df6512016-08-25 13:54:30 -07002217 }
2218
Mike Reed38992392019-07-30 10:48:15 -04002219 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002220
2221 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002222 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002223 }
2224
Mike Reed38992392019-07-30 10:48:15 -04002225 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002226}
2227
Mike Reedd5674082019-04-19 15:00:47 -04002228void SkCanvas::onDrawBehind(const SkPaint& paint) {
2229 SkIRect bounds;
2230 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2231 for (;;) {
2232 const MCRec* rec = (const MCRec*)iter.prev();
2233 if (!rec) {
2234 return; // no backimages, so nothing to draw
2235 }
2236 if (rec->fBackImage) {
2237 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2238 rec->fBackImage->fImage->width(),
2239 rec->fBackImage->fImage->height());
2240 break;
2241 }
2242 }
2243
Mike Reed38992392019-07-30 10:48:15 -04002244 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002245
2246 while (iter.next()) {
2247 SkBaseDevice* dev = iter.fDevice;
2248
Mike Reedd5674082019-04-19 15:00:47 -04002249 dev->save();
2250 // We use clipRegion because it is already defined to operate in dev-space
2251 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2252 // but we don't want that, so we undo that before calling in.
Michael Ludwig915b7792019-10-22 17:40:41 +00002253 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002254 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002255 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002256 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002257 }
2258
Mike Reed38992392019-07-30 10:48:15 -04002259 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002260}
2261
reed41af9662015-01-05 07:49:08 -08002262void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002263 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002264 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002265 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002266 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002267 return;
2268 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002269 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002270
Mike Reed38992392019-07-30 10:48:15 -04002271 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002272
2273 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002274 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002275 }
2276
Mike Reed38992392019-07-30 10:48:15 -04002277 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002278}
2279
bsalomonac3aa242016-08-19 11:25:19 -07002280void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2281 SkScalar sweepAngle, bool useCenter,
2282 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002283 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002284 if (paint.canComputeFastBounds()) {
2285 SkRect storage;
2286 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002287 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002288 return;
2289 }
bsalomonac3aa242016-08-19 11:25:19 -07002290 }
2291
Mike Reed38992392019-07-30 10:48:15 -04002292 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002293
2294 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002295 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002296 }
2297
Mike Reed38992392019-07-30 10:48:15 -04002298 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002299}
2300
reed41af9662015-01-05 07:49:08 -08002301void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002302 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002303 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002304 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2305 return;
2306 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002307 }
2308
2309 if (rrect.isRect()) {
2310 // call the non-virtual version
2311 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002312 return;
2313 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002314 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002315 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2316 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002317 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002318
Mike Reed38992392019-07-30 10:48:15 -04002319 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002320
2321 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002322 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002323 }
2324
Mike Reed38992392019-07-30 10:48:15 -04002325 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002326}
2327
Mike Reed822128b2017-02-28 16:41:03 -05002328void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002329 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002330 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002331 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2332 return;
2333 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002334 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002335
Mike Reed38992392019-07-30 10:48:15 -04002336 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002337
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002338 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002339 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002340 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002341
Mike Reed38992392019-07-30 10:48:15 -04002342 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002343}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002344
reed41af9662015-01-05 07:49:08 -08002345void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002346 if (!path.isFinite()) {
2347 return;
2348 }
2349
Mike Reed822128b2017-02-28 16:41:03 -05002350 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002351 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002352 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002353 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2354 return;
2355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002357
Mike Reed822128b2017-02-28 16:41:03 -05002358 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002359 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002360 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002361 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002362 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002363 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364
Mike Reed38992392019-07-30 10:48:15 -04002365 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366
2367 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002368 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 }
2370
Mike Reed38992392019-07-30 10:48:15 -04002371 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002372}
2373
reed262a71b2015-12-05 13:07:27 -08002374bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002375 if (!paint.getImageFilter()) {
2376 return false;
2377 }
2378
2379 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002380 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002381 return false;
2382 }
2383
2384 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2385 // Once we can filter and the filter will return a result larger than itself, we should be
2386 // able to remove this constraint.
2387 // skbug.com/4526
2388 //
2389 SkPoint pt;
2390 ctm.mapXY(x, y, &pt);
2391 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2392 return ir.contains(fMCRec->fRasterClip.getBounds());
2393}
2394
Mike Reedf441cfc2018-04-11 14:50:16 -04002395// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2396// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2397// null.
2398static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2399 if (paintParam) {
2400 *real = *paintParam;
2401 real->setStyle(SkPaint::kFill_Style);
2402 real->setPathEffect(nullptr);
2403 paintParam = real;
2404 }
2405 return paintParam;
2406}
2407
reeda85d4d02015-05-06 12:56:48 -07002408void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002409 SkPaint realPaint;
2410 paint = init_image_paint(&realPaint, paint);
2411
reeda85d4d02015-05-06 12:56:48 -07002412 SkRect bounds = SkRect::MakeXYWH(x, y,
2413 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002414 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002415 SkRect tmp = bounds;
2416 if (paint) {
2417 paint->computeFastBounds(tmp, &tmp);
2418 }
2419 if (this->quickReject(tmp)) {
2420 return;
2421 }
reeda85d4d02015-05-06 12:56:48 -07002422 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002423 // At this point we need a real paint object. If the caller passed null, then we should
2424 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2425 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2426 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002427
reeda2217ef2016-07-20 06:04:34 -07002428 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002429 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2430 *paint);
2431 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002432 special = this->getDevice()->makeSpecial(image);
2433 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002434 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002435 }
2436 }
2437
Mike Reed38992392019-07-30 10:48:15 -04002438 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002439
reeda85d4d02015-05-06 12:56:48 -07002440 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002441 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002442 if (special) {
2443 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002444 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002445 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002446 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002447 SkScalarRoundToInt(pt.fY), pnt,
2448 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002449 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002450 iter.fDevice->drawImageRect(
2451 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2452 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002453 }
reeda85d4d02015-05-06 12:56:48 -07002454 }
halcanary9d524f22016-03-29 09:03:52 -07002455
Mike Reed38992392019-07-30 10:48:15 -04002456 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002457}
2458
reed41af9662015-01-05 07:49:08 -08002459void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002460 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002461 SkPaint realPaint;
2462 paint = init_image_paint(&realPaint, paint);
2463
halcanary96fcdcc2015-08-27 07:41:13 -07002464 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002465 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002466 if (paint) {
2467 paint->computeFastBounds(dst, &storage);
2468 }
2469 if (this->quickReject(storage)) {
2470 return;
2471 }
reeda85d4d02015-05-06 12:56:48 -07002472 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002473 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002474
Mike Reed38992392019-07-30 10:48:15 -04002475 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002476
reeda85d4d02015-05-06 12:56:48 -07002477 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002478 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002479 }
halcanary9d524f22016-03-29 09:03:52 -07002480
Mike Reed38992392019-07-30 10:48:15 -04002481 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002482}
2483
reed41af9662015-01-05 07:49:08 -08002484void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002485 SkDEBUGCODE(bitmap.validate();)
2486
reed33366972015-10-08 09:22:02 -07002487 if (bitmap.drawsNothing()) {
2488 return;
2489 }
2490
Mike Reedf441cfc2018-04-11 14:50:16 -04002491 SkPaint realPaint;
2492 init_image_paint(&realPaint, paint);
2493 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002494
Mike Reed822128b2017-02-28 16:41:03 -05002495 SkRect bounds;
2496 bitmap.getBounds(&bounds);
2497 bounds.offset(x, y);
2498 bool canFastBounds = paint->canComputeFastBounds();
2499 if (canFastBounds) {
2500 SkRect storage;
2501 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002502 return;
2503 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002504 }
reed@google.com4b226022011-01-11 18:32:13 +00002505
reeda2217ef2016-07-20 06:04:34 -07002506 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002507 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2508 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002509 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002510 special = this->getDevice()->makeSpecial(bitmap);
2511 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002512 drawAsSprite = false;
2513 }
2514 }
2515
Mike Reed38992392019-07-30 10:48:15 -04002516 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002517
2518 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002519 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002520 if (special) {
reed262a71b2015-12-05 13:07:27 -08002521 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002522 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002523 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002524 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002525 SkScalarRoundToInt(pt.fY), pnt,
2526 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002527 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002528 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2529 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2530 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002531 }
reed33366972015-10-08 09:22:02 -07002532 }
msarettfbfa2582016-08-12 08:29:08 -07002533
Mike Reed38992392019-07-30 10:48:15 -04002534 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535}
2536
reed@google.com9987ec32011-09-07 11:57:52 +00002537// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002538void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002539 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002540 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002541 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002542 return;
2543 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002544
halcanary96fcdcc2015-08-27 07:41:13 -07002545 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002546 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002547 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2548 return;
2549 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002550 }
reed@google.com3d608122011-11-21 15:16:16 +00002551
reed@google.com33535f32012-09-25 15:37:50 +00002552 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002553 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002554 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002555 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002556
Mike Reed38992392019-07-30 10:48:15 -04002557 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002558
reed@google.com33535f32012-09-25 15:37:50 +00002559 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002560 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002561 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002562
Mike Reed38992392019-07-30 10:48:15 -04002563 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564}
2565
reed41af9662015-01-05 07:49:08 -08002566void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002567 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002568 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002569 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002570}
2571
reed4c21dc52015-06-25 12:32:03 -07002572void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2573 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002574 SkPaint realPaint;
2575 paint = init_image_paint(&realPaint, paint);
2576
halcanary96fcdcc2015-08-27 07:41:13 -07002577 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002578 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002579 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2580 return;
2581 }
reed@google.com3d608122011-11-21 15:16:16 +00002582 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002583 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002584
Mike Reed38992392019-07-30 10:48:15 -04002585 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002586
reed4c21dc52015-06-25 12:32:03 -07002587 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002588 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002589 }
halcanary9d524f22016-03-29 09:03:52 -07002590
Mike Reed38992392019-07-30 10:48:15 -04002591 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002592}
2593
reed41af9662015-01-05 07:49:08 -08002594void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2595 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002596 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002597 SkPaint realPaint;
2598 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002599
halcanary96fcdcc2015-08-27 07:41:13 -07002600 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002601 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002602 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2603 return;
2604 }
reed4c21dc52015-06-25 12:32:03 -07002605 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002606 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002607
Mike Reed38992392019-07-30 10:48:15 -04002608 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002609
reed4c21dc52015-06-25 12:32:03 -07002610 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002611 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002612 }
halcanary9d524f22016-03-29 09:03:52 -07002613
Mike Reed38992392019-07-30 10:48:15 -04002614 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002615}
2616
msarett16882062016-08-16 09:31:08 -07002617void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2618 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002619 SkPaint realPaint;
2620 paint = init_image_paint(&realPaint, paint);
2621
msarett16882062016-08-16 09:31:08 -07002622 if (nullptr == paint || paint->canComputeFastBounds()) {
2623 SkRect storage;
2624 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2625 return;
2626 }
2627 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002628 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002629
Mike Reed38992392019-07-30 10:48:15 -04002630 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002631
2632 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002633 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002634 }
2635
Mike Reed38992392019-07-30 10:48:15 -04002636 DRAW_END
msarett16882062016-08-16 09:31:08 -07002637}
2638
2639void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2640 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002641 SkPaint realPaint;
2642 paint = init_image_paint(&realPaint, paint);
2643
msarett16882062016-08-16 09:31:08 -07002644 if (nullptr == paint || paint->canComputeFastBounds()) {
2645 SkRect storage;
2646 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2647 return;
2648 }
2649 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002650 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002651
Mike Reed38992392019-07-30 10:48:15 -04002652 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002653
2654 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002655 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002656 }
2657
Mike Reed38992392019-07-30 10:48:15 -04002658 DRAW_END
msarett16882062016-08-16 09:31:08 -07002659}
2660
fmalita00d5c2c2014-08-21 08:53:26 -07002661void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2662 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002663 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002664 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002665 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002666 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002667 SkRect tmp;
2668 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2669 return;
2670 }
2671 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002672 }
2673
fmalita024f9962015-03-03 19:08:17 -08002674 // We cannot filter in the looper as we normally do, because the paint is
2675 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002676 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002677
fmalitaaa1b9122014-08-28 14:32:24 -07002678 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002679 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002680 }
2681
Mike Reed38992392019-07-30 10:48:15 -04002682 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002683}
2684
Mike Reed358fcad2018-11-23 15:27:51 -05002685// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002686void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002687 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2688 TRACE_EVENT0("skia", TRACE_FUNC);
2689 if (byteLength) {
2690 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002691 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002692 }
2693}
Mike Reed4f81bb72019-01-23 09:23:00 -05002694
fmalita00d5c2c2014-08-21 08:53:26 -07002695void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2696 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002697 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002698 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002699 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002700 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002701}
reed@google.come0d9ce82014-04-23 04:00:17 +00002702
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002703void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002704 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002705 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002706
2707 while (iter.next()) {
2708 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002709 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002710 }
2711
Mike Reed38992392019-07-30 10:48:15 -04002712 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002713}
2714
dandovb3c9d1c2014-08-12 08:34:29 -07002715void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002716 const SkPoint texCoords[4], SkBlendMode bmode,
2717 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002718 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002719 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002720 return;
2721 }
mtklein6cfa73a2014-08-13 13:33:49 -07002722
Mike Reedfaba3712016-11-03 14:45:31 -04002723 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002724}
2725
2726void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002727 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002728 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002729 // Since a patch is always within the convex hull of the control points, we discard it when its
2730 // bounding rectangle is completely outside the current clip.
2731 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002732 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002733 if (this->quickReject(bounds)) {
2734 return;
2735 }
mtklein6cfa73a2014-08-13 13:33:49 -07002736
Mike Reed38992392019-07-30 10:48:15 -04002737 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002738
dandovecfff212014-08-04 10:02:00 -07002739 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002740 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002741 }
mtklein6cfa73a2014-08-13 13:33:49 -07002742
Mike Reed38992392019-07-30 10:48:15 -04002743 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002744}
2745
reeda8db7282015-07-07 10:22:31 -07002746void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002747#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002748 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002749#endif
reede3b38ce2016-01-08 09:18:44 -08002750 RETURN_ON_NULL(dr);
2751 if (x || y) {
2752 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2753 this->onDrawDrawable(dr, &matrix);
2754 } else {
2755 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002756 }
2757}
2758
reeda8db7282015-07-07 10:22:31 -07002759void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002760#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002761 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002762#endif
reede3b38ce2016-01-08 09:18:44 -08002763 RETURN_ON_NULL(dr);
2764 if (matrix && matrix->isIdentity()) {
2765 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002766 }
reede3b38ce2016-01-08 09:18:44 -08002767 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002768}
2769
2770void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002771 // drawable bounds are no longer reliable (e.g. android displaylist)
2772 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002773 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002774}
2775
reed71c3c762015-06-24 10:29:17 -07002776void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002777 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002778 const SkRect* cull, const SkPaint* paint) {
2779 if (cull && this->quickReject(*cull)) {
2780 return;
2781 }
2782
2783 SkPaint pnt;
2784 if (paint) {
2785 pnt = *paint;
2786 }
halcanary9d524f22016-03-29 09:03:52 -07002787
Mike Reed38992392019-07-30 10:48:15 -04002788 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002789 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002790 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002791 }
Mike Reed38992392019-07-30 10:48:15 -04002792 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002793}
2794
reedf70b5312016-03-04 16:36:20 -08002795void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2796 SkASSERT(key);
2797
2798 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002799 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002800 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002801 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002802 }
Mike Reed38992392019-07-30 10:48:15 -04002803 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002804}
2805
Michael Ludwiga595f862019-08-27 15:25:49 -04002806void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2807 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002808 SkASSERT(r.isSorted());
2809
2810 // If this used a paint, it would be a filled color with blend mode, which does not
2811 // need to use an autodraw loop, so use SkDrawIter directly.
2812 if (this->quickReject(r)) {
2813 return;
2814 }
2815
Michael Ludwiga4b44882019-08-28 14:34:58 -04002816 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002817 SkDrawIter iter(this);
2818 while(iter.next()) {
2819 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2820 }
2821}
2822
2823void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2824 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2825 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002826 if (count <= 0) {
2827 // Nothing to draw
2828 return;
2829 }
2830
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002831 SkPaint realPaint;
2832 init_image_paint(&realPaint, paint);
2833
Michael Ludwiga4b44882019-08-28 14:34:58 -04002834 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2835 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2836 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2837 // or we need it for the autolooper (since it greatly improves image filter perf).
2838 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2839 bool setBoundsValid = count == 1 || needsAutoLooper;
2840 SkRect setBounds = imageSet[0].fDstRect;
2841 if (imageSet[0].fMatrixIndex >= 0) {
2842 // Account for the per-entry transform that is applied prior to the CTM when drawing
2843 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002844 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002845 if (needsAutoLooper) {
2846 for (int i = 1; i < count; ++i) {
2847 SkRect entryBounds = imageSet[i].fDstRect;
2848 if (imageSet[i].fMatrixIndex >= 0) {
2849 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2850 }
2851 setBounds.joinPossiblyEmptyRect(entryBounds);
2852 }
2853 }
2854
2855 // If we happen to have the draw bounds, though, might as well check quickReject().
2856 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2857 SkRect tmp;
2858 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2859 return;
2860 }
2861 }
2862
2863 if (needsAutoLooper) {
2864 SkASSERT(setBoundsValid);
2865 DRAW_BEGIN(realPaint, &setBounds)
2866 while (iter.next()) {
2867 iter.fDevice->drawEdgeAAImageSet(
2868 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2869 }
2870 DRAW_END
2871 } else {
2872 this->predrawNotify();
2873 SkDrawIter iter(this);
2874 while(iter.next()) {
2875 iter.fDevice->drawEdgeAAImageSet(
2876 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2877 }
2878 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002879}
2880
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881//////////////////////////////////////////////////////////////////////////////
2882// These methods are NOT virtual, and therefore must call back into virtual
2883// methods, rather than actually drawing themselves.
2884//////////////////////////////////////////////////////////////////////////////
2885
reed374772b2016-10-05 17:33:02 -07002886void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002887 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002889 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002890 this->drawPaint(paint);
2891}
2892
2893void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002894 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002895 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2896}
2897
Mike Reed3661bc92017-02-22 13:21:42 -05002898void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002899 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002900 pts[0].set(x0, y0);
2901 pts[1].set(x1, y1);
2902 this->drawPoints(kLines_PointMode, 2, pts, paint);
2903}
2904
Mike Reed3661bc92017-02-22 13:21:42 -05002905void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002906 if (radius < 0) {
2907 radius = 0;
2908 }
2909
2910 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002911 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002912 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002913}
2914
2915void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2916 const SkPaint& paint) {
2917 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002918 SkRRect rrect;
2919 rrect.setRectXY(r, rx, ry);
2920 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002921 } else {
2922 this->drawRect(r, paint);
2923 }
2924}
2925
reed@android.com8a1c16f2008-12-17 15:59:43 +00002926void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2927 SkScalar sweepAngle, bool useCenter,
2928 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002929 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002930 if (oval.isEmpty() || !sweepAngle) {
2931 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002932 }
bsalomon21af9ca2016-08-25 12:29:23 -07002933 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934}
2935
reed@android.comf76bacf2009-05-13 14:00:33 +00002936///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002937#ifdef SK_DISABLE_SKPICTURE
2938void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002939
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002940
2941void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2942 const SkPaint* paint) {}
2943#else
Mike Klein88d90712018-01-27 17:30:04 +00002944/**
2945 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2946 * against the playback cost of recursing into the subpicture to get at its actual ops.
2947 *
2948 * For now we pick a conservatively small value, though measurement (and other heuristics like
2949 * the type of ops contained) may justify changing this value.
2950 */
2951#define kMaxPictureOpsToUnrollInsteadOfRef 1
2952
reedd5fa1a42014-08-09 11:08:05 -07002953void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002954 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002955 RETURN_ON_NULL(picture);
2956
reede3b38ce2016-01-08 09:18:44 -08002957 if (matrix && matrix->isIdentity()) {
2958 matrix = nullptr;
2959 }
Mike Klein88d90712018-01-27 17:30:04 +00002960 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2961 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2962 picture->playback(this);
2963 } else {
2964 this->onDrawPicture(picture, matrix, paint);
2965 }
reedd5fa1a42014-08-09 11:08:05 -07002966}
robertphillips9b14f262014-06-04 05:40:44 -07002967
reedd5fa1a42014-08-09 11:08:05 -07002968void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2969 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002970 if (!paint || paint->canComputeFastBounds()) {
2971 SkRect bounds = picture->cullRect();
2972 if (paint) {
2973 paint->computeFastBounds(bounds, &bounds);
2974 }
2975 if (matrix) {
2976 matrix->mapRect(&bounds);
2977 }
2978 if (this->quickReject(bounds)) {
2979 return;
2980 }
2981 }
2982
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002983 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002984 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002985}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002986#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002987
reed@android.com8a1c16f2008-12-17 15:59:43 +00002988///////////////////////////////////////////////////////////////////////////////
2989///////////////////////////////////////////////////////////////////////////////
2990
reed3aafe112016-08-18 12:45:34 -07002991SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002992 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002993
2994 SkASSERT(canvas);
2995
reed3aafe112016-08-18 12:45:34 -07002996 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002997 fDone = !fImpl->next();
2998}
2999
3000SkCanvas::LayerIter::~LayerIter() {
3001 fImpl->~SkDrawIter();
3002}
3003
3004void SkCanvas::LayerIter::next() {
3005 fDone = !fImpl->next();
3006}
3007
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003008SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003009 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003010}
3011
3012const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003013 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003014}
3015
3016const SkPaint& SkCanvas::LayerIter::paint() const {
3017 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003018 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003019 paint = &fDefaultPaint;
3020 }
3021 return *paint;
3022}
3023
Mike Reedca37f322018-03-08 13:22:16 -05003024SkIRect SkCanvas::LayerIter::clipBounds() const {
3025 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003026}
3027
Michael Ludwig915b7792019-10-22 17:40:41 +00003028int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3029int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003030
3031///////////////////////////////////////////////////////////////////////////////
3032
Brian Osmane8a98632019-04-10 10:26:10 -04003033SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3034SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3035SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3036SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3037
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003038SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3039 const SkRect& dstRect, int matrixIndex, float alpha,
3040 unsigned aaFlags, bool hasClip)
3041 : fImage(std::move(image))
3042 , fSrcRect(srcRect)
3043 , fDstRect(dstRect)
3044 , fMatrixIndex(matrixIndex)
3045 , fAlpha(alpha)
3046 , fAAFlags(aaFlags)
3047 , fHasClip(hasClip) {}
3048
3049SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3050 const SkRect& dstRect, float alpha, unsigned aaFlags)
3051 : fImage(std::move(image))
3052 , fSrcRect(srcRect)
3053 , fDstRect(dstRect)
3054 , fAlpha(alpha)
3055 , fAAFlags(aaFlags) {}
3056
3057///////////////////////////////////////////////////////////////////////////////
3058
Mike Reed5df49342016-11-12 08:06:55 -06003059std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003060 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003061 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003062 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003063 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003064
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003065 SkBitmap bitmap;
3066 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003067 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003068 }
Mike Reed12f77342017-11-08 11:19:52 -05003069
3070 return props ?
3071 skstd::make_unique<SkCanvas>(bitmap, *props) :
3072 skstd::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003073}
reedd5fa1a42014-08-09 11:08:05 -07003074
3075///////////////////////////////////////////////////////////////////////////////
3076
Florin Malitaee424ac2016-12-01 12:47:59 -05003077SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003078 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003079
Florin Malita439ace92016-12-02 12:05:41 -05003080SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003081 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003082
Herb Derbyefe39bc2018-05-01 17:06:20 -04003083SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003084 : INHERITED(device) {}
3085
Florin Malitaee424ac2016-12-01 12:47:59 -05003086SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3087 (void)this->INHERITED::getSaveLayerStrategy(rec);
3088 return kNoLayer_SaveLayerStrategy;
3089}
3090
Mike Reed148b7fd2018-12-18 17:38:18 -05003091bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3092 return false;
3093}
3094
Florin Malitaee424ac2016-12-01 12:47:59 -05003095///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003096
reed73603f32016-09-20 08:42:38 -07003097static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3098static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3099static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3100static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3101static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3102static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003103
3104///////////////////////////////////////////////////////////////////////////////////////////////////
3105
3106SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3107 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003108 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003109 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
Michael Ludwig915b7792019-10-22 17:40:41 +00003110 SkIPoint origin = dev->getOrigin();
3111 SkMatrix ctm = this->getTotalMatrix();
3112 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3113
3114 SkIRect clip = fMCRec->fRasterClip.getBounds();
3115 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003116 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003117 clip.setEmpty();
3118 }
3119
Michael Ludwig915b7792019-10-22 17:40:41 +00003120 fAllocator->updateHandle(handle, ctm, clip);
Mike Reed356f7c22017-01-10 11:58:39 -05003121 return handle;
3122 }
3123 return nullptr;
3124}
3125
3126static bool install(SkBitmap* bm, const SkImageInfo& info,
3127 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003128 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003129}
3130
3131SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3132 SkBitmap* bm) {
3133 SkRasterHandleAllocator::Rec rec;
3134 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3135 return nullptr;
3136 }
3137 return rec.fHandle;
3138}
3139
3140std::unique_ptr<SkCanvas>
3141SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3142 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003143 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003144 return nullptr;
3145 }
3146
3147 SkBitmap bm;
3148 Handle hndl;
3149
3150 if (rec) {
3151 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3152 } else {
3153 hndl = alloc->allocBitmap(info, &bm);
3154 }
3155 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3156}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003157
3158///////////////////////////////////////////////////////////////////////////////////////////////////